Account functionality extended, API authentication implemented

- Can now add a second email address to your account and delete one of 
your email addresses
- Reset password now works
- Resetting the password of an account without a password set will set 
one
- Email templates overhauled
- Login tokens limited to close previously devastating ($800 database 
bill) security hole
- Login with REST API now works
- Once logged in, authentication of API calls with token works
- Creatures can now be fetched using the API
This commit is contained in:
Stefan Zermatten
2022-02-10 19:02:18 +02:00
parent 3948d20f46
commit 359f18988c
27 changed files with 852 additions and 11 deletions

View File

@@ -0,0 +1,34 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const addEmail = new ValidatedMethod({
name: 'users.addEmail',
validate: new SimpleSchema({
email: {
type: String,
regEx: SimpleSchema.RegEx.Email,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 1,
timeInterval: 5000,
},
run({email}){
const userId = Meteor.userId();
const user = Meteor.users.findOne(userId);
if (!user) throw new Meteor.Error('No user',
'You must be logged in to add an email address');
if (user.emails && user.emails.length >= 2){
throw new Meteor.Error('Emails full',
'You may only have up to 2 email addresses per account');
}
if (Meteor.isServer){
Accounts.addEmail(userId, email);
Accounts.sendVerificationEmail(userId, email);
}
}
});
export default addEmail;

View File

@@ -0,0 +1,61 @@
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Libraries, {removeLibaryWork} from '/imports/api/library/Libraries.js';
import Creatures from '/imports/api/creature/creatures/Creatures.js';
import {removeCreatureWork} from '/imports/api/creature/creatures/methods/removeCreature.js';
Meteor.users.deleteMyAccount = new ValidatedMethod({
name: 'users.deleteMyAccount',
validate: null,
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 1,
timeInterval: 5000,
},
run(){
let userId = Meteor.userId();
if (!userId) throw new Meteor.Error('No user',
'You must be logged in to delete your account');
// Delete all creatures
let creatures = Creatures.find({owner: userId}, {fields: {_id: 1}}).fetch();
creatures.forEach(creature => removeCreatureWork(creature._id));
// Remove permissions from all creatures
Creatures.update({
$or: [
{writers: userId},
{readers: userId},
],
}, {
$pull: {
writers: userId,
readers: userId
},
}, {
multi: true,
});
// Delete all libraries
let libraries = Libraries.find({owner: userId}, {fields: {_id: 1}}).fetch();
libraries.forEach(library => removeLibaryWork(library._id));
// Remove permissions from all creatures
Libraries.update({
$or: [
{writers: userId},
{readers: userId},
],
}, {
$pull: {
writers: userId,
readers: userId
},
}, {
multi: true,
});
// delete the account
Meteor.users.remove(userId);
}
});

View File

@@ -0,0 +1,20 @@
// Adds accounts-patreon support to bozhao:link-accounts
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
export default function linkWithPatreon(options, callback) {
if (!Meteor.userId()) {
throw new Meteor.Error(402, 'Please login to an existing account before link.');
}
if (!Package['patreon-oauth']) {
throw new Meteor.Error(403, 'Please include patreon-oauth package');
}
if (!callback && typeof options === 'function') {
callback = options;
options = null;
}
const credentialRequestCompleteCallback = Accounts.oauth.linkCredentialRequestCompleteHandler(callback);
Package['patreon-oauth'].Patreon.requestCredential(options, credentialRequestCompleteCallback);
}

View File

@@ -0,0 +1,37 @@
import SimpleSchema from 'simpl-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
const removeEmail = new ValidatedMethod({
name: 'users.removeEmail',
validate: new SimpleSchema({
email: {
type: String,
regEx: SimpleSchema.RegEx.Email,
},
}).validator(),
mixins: [RateLimiterMixin],
rateLimit: {
numRequests: 1,
timeInterval: 5000,
},
run({email}){
const userId = Meteor.userId();
const user = Meteor.users.findOne(userId);
if (!user) throw new Meteor.Error('No user',
'You must be logged in to remove an email address');
if (!user.emails){
throw new Meteor.Error('No email to remove',
'No email addresses are associated with this account');
}
if (user.emails.length == 1){
throw new Meteor.Error('Can\'t remove last email',
'You may not remove the last email address from your account');
}
if (Meteor.isServer){
Accounts.removeEmail(userId, email);
}
}
});
export default removeEmail;