[Experimental] add an option for brainlet instance owners to require a special code sign up
This commit is contained in:
parent
6d142767b0
commit
bc75e3f24c
4 changed files with 94 additions and 12 deletions
|
@ -120,7 +120,7 @@ app.get('/category/:category/info', [
|
|||
users: users
|
||||
}
|
||||
});
|
||||
}));
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.get('/category/list', authenticateEndpoint(async (req, res, user) => {
|
||||
let count = parseInt(req.query.count);
|
||||
|
@ -136,6 +136,6 @@ app.get('/category/list', authenticateEndpoint(async (req, res, user) => {
|
|||
message: 'SUCCESS_CATEGORY_LIST_FETCHED',
|
||||
categories
|
||||
});
|
||||
}));
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
module.exports = app;
|
|
@ -22,11 +22,26 @@ const createAccountLimiter = rateLimit({
|
|||
message: "You are being rate limited"
|
||||
});
|
||||
|
||||
app.get('/account/create/info', async (req, res) => {
|
||||
const restrictions = config.restrictions.signup;
|
||||
let requiresCode = false;
|
||||
if (restrictions && restrictions.specialCode) {
|
||||
requiresCode = true;
|
||||
}
|
||||
|
||||
res.json({
|
||||
error: false,
|
||||
message: 'SUCCESS_ACCOUNT_CREATE_INFO_FETCH',
|
||||
requiresSpecialCode: requiresCode
|
||||
});
|
||||
});
|
||||
|
||||
app.post('/account/create', [
|
||||
createAccountLimiter,
|
||||
body('username').not().isEmpty().trim().isLength({ min: 3, max: 32 }).isAlphanumeric(),
|
||||
body('email').not().isEmpty().isEmail().normalizeEmail(),
|
||||
body('password').not().isEmpty().isLength({ min: 8, max: 128 })
|
||||
body('password').not().isEmpty().isLength({ min: 8, max: 128 }),
|
||||
body('specialCode').optional().isLength({ min: 12, max: 12 }).isAlphanumeric()
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
|
@ -34,6 +49,22 @@ app.post('/account/create', [
|
|||
res.status(400).json({ error: true, message: 'ERROR_REQUEST_INVALID_DATA', errors: errors.array() });
|
||||
return;
|
||||
}
|
||||
|
||||
const restrictions = config.restrictions.signup;
|
||||
if (restrictions && restrictions.specialCode) {
|
||||
const passedSpecialCode = req.body.specialCode;
|
||||
const specialCode = restrictions.specialCode;
|
||||
|
||||
if (passedSpecialCode && specialCode) {
|
||||
if (specialCode !== passedSpecialCode) {
|
||||
res.status(401).json({ error: true, message: 'ERROR_REQUEST_SPECIAL_CODE_MISSING', errors: [{ msg: 'No specialCode passed', param: 'specialCode', location: 'body' }] });
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
res.status(401).json({ error: true, message: 'ERROR_REQUEST_SPECIAL_CODE_MISSING', errors: [{ msg: 'No specialCode passed', param: 'specialCode', location: 'body' }] });
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const username = req.body.username;
|
||||
|
||||
|
@ -171,7 +202,7 @@ app.get('/user/:userid/info', [
|
|||
message: 'SUCCESS_USER_DATA_FETCHED',
|
||||
user: await otherUser.getPublicObject(),
|
||||
});
|
||||
}));
|
||||
}, undefined, config.roleMap.USER));
|
||||
|
||||
app.post('/browser/token/clear', authenticateEndpoint((req, res, user) => {
|
||||
res.clearCookie('token');
|
||||
|
|
|
@ -47,13 +47,26 @@
|
|||
</md-field>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="mode === 'SPECIAL_CODE'">
|
||||
<div>
|
||||
<p>
|
||||
The owner of this Brainlet instance has made it so that signing up requires a special code.
|
||||
</p>
|
||||
<md-field>
|
||||
<label>Special code</label>
|
||||
<md-input v-model="specialCodeInput" type="password"></md-input>
|
||||
</md-field>
|
||||
</div>
|
||||
</div>
|
||||
</md-card-content>
|
||||
|
||||
<md-card-actions>
|
||||
<md-button v-if="mode === 'SIGNUP'" @click="mode='LOGIN'" class="md-dense">Log in instead</md-button>
|
||||
<md-button v-if="mode === 'LOGIN'" @click="mode='SIGNUP'" class="md-dense">Sign up instead</md-button>
|
||||
|
||||
<md-button v-if="mode === 'SIGNUP'" class="md-dense md-raised md-primary" @click="performAccountCreation()">Sign up</md-button>
|
||||
<md-button v-if="mode === 'SPECIAL_CODE'" class="md-dense" @click="mode='SIGNUP'">Go back</md-button>
|
||||
<md-button v-if="mode === 'SIGNUP' || mode === 'SPECIAL_CODE'" class="md-dense md-raised md-primary" @click="performAccountCreation()">Sign up</md-button>
|
||||
|
||||
<md-button v-if="mode === 'LOGIN'" class="md-dense md-raised md-primary" @click="performTokenCreation()">Log in</md-button>
|
||||
|
||||
<md-button v-if="mode === 'MANAGE'" class="md-dense md-raised" @click="performTokenRemoval()">Log out</md-button>
|
||||
|
|
|
@ -30,6 +30,9 @@ const getSignupMessageFromError = (json) => {
|
|||
case 'email': {
|
||||
return 'Invalid email.';
|
||||
}
|
||||
case 'specialCode': {
|
||||
return 'Invalid special code.';
|
||||
}
|
||||
|
||||
default: {
|
||||
return 'Invalid value sent to server. Something went wrong.';
|
||||
|
@ -58,13 +61,15 @@ const app = new Vue({
|
|||
usernameInput: '',
|
||||
emailInput: '',
|
||||
passwordInput: '',
|
||||
specialCodeInput: '',
|
||||
mode: 'SIGNUP',
|
||||
showSnackbarNotification: false,
|
||||
snackbarNotification: '',
|
||||
snackbarNotificationDuration: 999999,
|
||||
snackbarButtonText: 'Ok',
|
||||
successfulLogin: false,
|
||||
loggedInUser: {}
|
||||
loggedInUser: {},
|
||||
requiresSpecialCode: null
|
||||
},
|
||||
mounted: async function() {
|
||||
const res = await fetch(`${window.location.origin}/api/v1/users/current/info`, {
|
||||
|
@ -86,7 +91,21 @@ const app = new Vue({
|
|||
this.successfulLogin = true;
|
||||
}
|
||||
} else {
|
||||
this.mode = 'SIGNUP';
|
||||
const resInfo = await fetch(`${window.location.origin}/api/v1/users/account/create/info`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
});
|
||||
|
||||
if (resInfo.ok) {
|
||||
const json = await resInfo.json();
|
||||
this.requiresSpecialCode = json.requiresSpecialCode;
|
||||
|
||||
this.mode = 'SIGNUP';
|
||||
} else {
|
||||
this.mode = '_ERROR';
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -104,6 +123,9 @@ const app = new Vue({
|
|||
case 'MANAGE': {
|
||||
return this.loggedInUser.username || 'Unknown account';
|
||||
}
|
||||
case 'SPECIAL_CODE': {
|
||||
return 'Just one more step'
|
||||
}
|
||||
case 'NONE': {
|
||||
return '';
|
||||
}
|
||||
|
@ -192,17 +214,33 @@ const app = new Vue({
|
|||
}
|
||||
},
|
||||
performAccountCreation: async function() {
|
||||
let jsonData = {
|
||||
username: this.usernameInput,
|
||||
email: this.emailInput,
|
||||
password: this.passwordInput
|
||||
};
|
||||
|
||||
if (this.requiresSpecialCode) {
|
||||
if (this.mode === 'SIGNUP') {
|
||||
this.mode = 'SPECIAL_CODE';
|
||||
return;
|
||||
} else if (this.mode !== 'SPECIAL_CODE') {
|
||||
return;
|
||||
}
|
||||
|
||||
jsonData = {
|
||||
specialCode: this.specialCodeInput,
|
||||
...jsonData
|
||||
}
|
||||
}
|
||||
|
||||
const res = await fetch(`${window.location.origin}/api/v1/users/account/create`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
username: this.usernameInput,
|
||||
email: this.emailInput,
|
||||
password: this.passwordInput
|
||||
})
|
||||
body: JSON.stringify(jsonData)
|
||||
});
|
||||
|
||||
const json = await res.json();
|
||||
|
|
Reference in a new issue