mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 15:13:21 +00:00
Add /users/me routes
Signed-off-by: Alexandre Bodin <bodin.alex@gmail.com>
This commit is contained in:
parent
cf4c70b01e
commit
851ba201ac
@ -148,18 +148,12 @@
|
||||
{
|
||||
"method": "GET",
|
||||
"path": "/users/me",
|
||||
"handler": "User.getAuthenticatedUser",
|
||||
"config": {
|
||||
"policies": []
|
||||
}
|
||||
"handler": "authenticated-user.getMe"
|
||||
},
|
||||
{
|
||||
"method": "PUT",
|
||||
"path": "/users/me",
|
||||
"handler": "User.updateAuthenticatedUser",
|
||||
"config": {
|
||||
"policies": []
|
||||
}
|
||||
"handler": "authenticated-user.updateMe"
|
||||
},
|
||||
{
|
||||
"method": "POST",
|
||||
|
||||
37
packages/strapi-admin/controllers/authenticated-user.js
Normal file
37
packages/strapi-admin/controllers/authenticated-user.js
Normal file
@ -0,0 +1,37 @@
|
||||
'use strict';
|
||||
|
||||
const { validateProfileUpdateInput } = require('../validation/user');
|
||||
|
||||
module.exports = {
|
||||
async getMe(ctx) {
|
||||
if (!ctx.state.user || !ctx.state.isAuthenticatedAdmin) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
const userInfo = strapi.admin.services.user.sanitizeUser(ctx.state.user);
|
||||
|
||||
ctx.body = {
|
||||
data: userInfo,
|
||||
};
|
||||
},
|
||||
|
||||
async updateMe(ctx) {
|
||||
const input = ctx.request.body;
|
||||
|
||||
if (!ctx.state.user || !ctx.state.isAuthenticatedAdmin) {
|
||||
return ctx.forbidden();
|
||||
}
|
||||
|
||||
try {
|
||||
await validateProfileUpdateInput(input);
|
||||
} catch (err) {
|
||||
return ctx.badRequest('ValidationError', err);
|
||||
}
|
||||
|
||||
const updatedUser = strapi.admin.services.user.update({ id: ctx.state.user.id }, input);
|
||||
|
||||
ctx.body = {
|
||||
data: strapi.admin.services.user.sanitizeUser(updatedUser),
|
||||
};
|
||||
},
|
||||
};
|
||||
@ -43,28 +43,4 @@ module.exports = {
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async getAuthenticatedUser(ctx) {
|
||||
ctx.body = {
|
||||
data: {
|
||||
email: '',
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
username: '',
|
||||
roles: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
async updateAuthenticatedUser(ctx) {
|
||||
ctx.body = {
|
||||
data: {
|
||||
email: '',
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
username: '',
|
||||
roles: [],
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@ -44,6 +44,7 @@ module.exports = strapi => ({
|
||||
|
||||
ctx.state.admin = admin;
|
||||
ctx.state.user = admin;
|
||||
ctx.state.isAuthenticatedAdmin = true;
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +79,36 @@ describe('User', () => {
|
||||
});
|
||||
|
||||
describe('update', () => {
|
||||
test('Hash password', async () => {
|
||||
const hash = 'aoizdnoaizndoainzodiaz';
|
||||
|
||||
const params = { id: 1 };
|
||||
const input = { email: 'test@strapi.io', password: '123' };
|
||||
|
||||
const update = jest.fn((_, user) => Promise.resolve(user));
|
||||
const hashPassword = jest.fn(() => Promise.resolve(hash));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return { update };
|
||||
},
|
||||
admin: {
|
||||
services: {
|
||||
auth: { hashPassword },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const result = await userService.update(params, input);
|
||||
|
||||
expect(hashPassword).toHaveBeenCalledWith(input.password);
|
||||
expect(update).toHaveBeenCalledWith(params, { email: input.email, password: hash });
|
||||
expect(result).toEqual({
|
||||
email: 'test@strapi.io',
|
||||
password: 'aoizdnoaizndoainzodiaz',
|
||||
});
|
||||
});
|
||||
|
||||
test('Forwards call to the query layer', async () => {
|
||||
const user = {
|
||||
email: 'test@strapi.io',
|
||||
@ -259,10 +289,9 @@ describe('User', () => {
|
||||
expect(userService.register(input)).rejects.toThrowError('Invalid registration info');
|
||||
});
|
||||
|
||||
test('Create a password hash', async () => {
|
||||
test('Calls udpate service', async () => {
|
||||
const findOne = jest.fn(() => Promise.resolve({ id: 1 }));
|
||||
const update = jest.fn(user => Promise.resolve(user));
|
||||
const hashPassword = jest.fn(() => Promise.resolve('123456789'));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
@ -273,7 +302,6 @@ describe('User', () => {
|
||||
admin: {
|
||||
services: {
|
||||
user: { update },
|
||||
auth: { hashPassword },
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -289,54 +317,15 @@ describe('User', () => {
|
||||
|
||||
await userService.register(input);
|
||||
|
||||
expect(hashPassword).toHaveBeenCalledWith('Test1234');
|
||||
expect(update).toHaveBeenCalledWith(
|
||||
{ id: 1 },
|
||||
expect.objectContaining({ password: '123456789' })
|
||||
);
|
||||
});
|
||||
|
||||
test('Set user firstname and lastname', async () => {
|
||||
const findOne = jest.fn(() => Promise.resolve({ id: 1 }));
|
||||
const update = jest.fn(user => Promise.resolve(user));
|
||||
const hashPassword = jest.fn(() => Promise.resolve('123456789'));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return {
|
||||
findOne,
|
||||
};
|
||||
},
|
||||
admin: {
|
||||
services: {
|
||||
user: { update },
|
||||
auth: { hashPassword },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const input = {
|
||||
registrationToken: '123',
|
||||
userInfo: {
|
||||
firstname: 'test',
|
||||
lastname: 'Strapi',
|
||||
password: 'Test1234',
|
||||
},
|
||||
};
|
||||
|
||||
await userService.register(input);
|
||||
|
||||
expect(hashPassword).toHaveBeenCalledWith('Test1234');
|
||||
expect(update).toHaveBeenCalledWith(
|
||||
{ id: 1 },
|
||||
expect.objectContaining({ firstname: 'test', lastname: 'Strapi' })
|
||||
expect.objectContaining({ firstname: 'test', lastname: 'Strapi', password: 'Test1234' })
|
||||
);
|
||||
});
|
||||
|
||||
test('Set user to active', async () => {
|
||||
const findOne = jest.fn(() => Promise.resolve({ id: 1 }));
|
||||
const update = jest.fn(user => Promise.resolve(user));
|
||||
const hashPassword = jest.fn(() => Promise.resolve('123456789'));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
@ -347,7 +336,6 @@ describe('User', () => {
|
||||
admin: {
|
||||
services: {
|
||||
user: { update },
|
||||
auth: { hashPassword },
|
||||
},
|
||||
},
|
||||
};
|
||||
@ -363,7 +351,41 @@ describe('User', () => {
|
||||
|
||||
await userService.register(input);
|
||||
|
||||
expect(hashPassword).toHaveBeenCalledWith('Test1234');
|
||||
expect(update).toHaveBeenCalledWith({ id: 1 }, expect.objectContaining({ isActive: true }));
|
||||
});
|
||||
|
||||
test('Reset registrationToken', async () => {
|
||||
const findOne = jest.fn(() => Promise.resolve({ id: 1 }));
|
||||
const update = jest.fn(user => Promise.resolve(user));
|
||||
|
||||
global.strapi = {
|
||||
query() {
|
||||
return {
|
||||
findOne,
|
||||
};
|
||||
},
|
||||
admin: {
|
||||
services: {
|
||||
user: { update },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const input = {
|
||||
registrationToken: '123',
|
||||
userInfo: {
|
||||
firstname: 'test',
|
||||
lastname: 'Strapi',
|
||||
password: 'Test1234',
|
||||
},
|
||||
};
|
||||
|
||||
await userService.register(input);
|
||||
|
||||
expect(update).toHaveBeenCalledWith(
|
||||
{ id: 1 },
|
||||
expect.objectContaining({ registrationToken: null })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -32,6 +32,16 @@ const create = async attributes => {
|
||||
* @returns {Promise<user>}
|
||||
*/
|
||||
const update = async (params, attributes) => {
|
||||
// hash password if a new one is sent
|
||||
if (_.has(attributes, 'password')) {
|
||||
const hashedPassword = await strapi.admin.services.auth.hashPassword(attributes.password);
|
||||
|
||||
return strapi.query('user', 'admin').update(params, {
|
||||
...attributes,
|
||||
password: hashedPassword,
|
||||
});
|
||||
}
|
||||
|
||||
return strapi.query('user', 'admin').update(params, attributes);
|
||||
};
|
||||
|
||||
@ -72,12 +82,10 @@ const register = async ({ registrationToken, userInfo }) => {
|
||||
throw strapi.errors.badRequest('Invalid registration info');
|
||||
}
|
||||
|
||||
const hashedPassword = await strapi.admin.services.auth.hashPassword(userInfo.password);
|
||||
|
||||
return strapi.admin.services.user.update(
|
||||
{ id: matchingUser.id },
|
||||
{
|
||||
password: hashedPassword,
|
||||
password: userInfo.password,
|
||||
firstname: userInfo.firstname,
|
||||
lastname: userInfo.lastname,
|
||||
registrationToken: null,
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { yup, formatYupErrors } = require('strapi-utils');
|
||||
const validators = require('./common-validators');
|
||||
|
||||
const registrationSchema = yup
|
||||
.object()
|
||||
@ -9,21 +10,9 @@ const registrationSchema = yup
|
||||
userInfo: yup
|
||||
.object()
|
||||
.shape({
|
||||
firstname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
lastname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8)
|
||||
.matches(/[a-z]/, '${path} must contain at least one lowercase character')
|
||||
.matches(/[A-Z]/, '${path} must contain at least one uppercase character')
|
||||
.matches(/\d/, '${path} must contain at least one number')
|
||||
.required(),
|
||||
firstname: validators.firstname,
|
||||
lastname: validators.lastname,
|
||||
password: validators.password,
|
||||
})
|
||||
.required()
|
||||
.noUnknown(),
|
||||
|
||||
23
packages/strapi-admin/validation/common-validators.js
Normal file
23
packages/strapi-admin/validation/common-validators.js
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
|
||||
const { yup } = require('strapi-utils');
|
||||
|
||||
const validators = {
|
||||
firstname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
lastname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
password: yup
|
||||
.string()
|
||||
.min(8)
|
||||
.matches(/[a-z]/, '${path} must contain at least one lowercase character')
|
||||
.matches(/[A-Z]/, '${path} must contain at least one uppercase character')
|
||||
.matches(/\d/, '${path} must contain at least one number')
|
||||
.required(),
|
||||
};
|
||||
|
||||
module.exports = validators;
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const { yup, formatYupErrors } = require('strapi-utils');
|
||||
const validators = require('./common-validators');
|
||||
|
||||
const handleReject = error => Promise.reject(formatYupErrors(error));
|
||||
|
||||
@ -11,14 +12,8 @@ const userCreationSchema = yup
|
||||
.string()
|
||||
.email()
|
||||
.required(),
|
||||
firstname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
lastname: yup
|
||||
.string()
|
||||
.min(1)
|
||||
.required(),
|
||||
firstname: validators.firstname,
|
||||
lastname: validators.lastname,
|
||||
roles: yup.array(), // FIXME: set min to 1 once the create role API is created,
|
||||
})
|
||||
.noUnknown();
|
||||
@ -27,6 +22,27 @@ const validateUserCreationInput = data => {
|
||||
return userCreationSchema.validate(data, { strict: true, abortEarly: false }).catch(handleReject);
|
||||
};
|
||||
|
||||
const profileUpdateSchema = yup
|
||||
.object()
|
||||
.shape({
|
||||
email: yup
|
||||
.string()
|
||||
.email()
|
||||
.required(),
|
||||
firstname: validators.firstname,
|
||||
lastname: validators.lastname,
|
||||
username: yup.string().min(1),
|
||||
password: validators.password,
|
||||
})
|
||||
.noUnknown();
|
||||
|
||||
const validateProfileUpdateInput = data => {
|
||||
return profileUpdateSchema
|
||||
.validate(data, { strict: true, abortEarly: false })
|
||||
.catch(handleReject);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
validateUserCreationInput,
|
||||
validateProfileUpdateInput,
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user