mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #223 from Human-Connection/212-change-password
Change password in the backend
This commit is contained in:
commit
af03b4958b
@ -56,11 +56,12 @@ const permissions = shield({
|
||||
CreateBadge: isAdmin,
|
||||
UpdateBadge: isAdmin,
|
||||
DeleteBadge: isAdmin,
|
||||
// addFruitToBasket: isAuthenticated
|
||||
follow: isAuthenticated,
|
||||
unfollow: isAuthenticated,
|
||||
shout: isAuthenticated,
|
||||
unshout: isAuthenticated,
|
||||
|
||||
changePassword: isAuthenticated,
|
||||
enable: isModerator,
|
||||
disable: isModerator
|
||||
// CreateUser: allow,
|
||||
|
||||
@ -30,22 +30,71 @@ export default {
|
||||
// throw new Error('Already logged in.')
|
||||
// }
|
||||
const session = driver.session()
|
||||
return session.run(
|
||||
const result = await session.run(
|
||||
'MATCH (user:User {email: $userEmail}) ' +
|
||||
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role} as user LIMIT 1', {
|
||||
'RETURN user {.id, .slug, .name, .avatar, .email, .password, .role} as user LIMIT 1',
|
||||
{
|
||||
userEmail: email
|
||||
})
|
||||
.then(async (result) => {
|
||||
session.close()
|
||||
const [currentUser] = await result.records.map(function (record) {
|
||||
return record.get('user')
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
if (currentUser && await bcrypt.compareSync(password, currentUser.password)) {
|
||||
delete currentUser.password
|
||||
return encode(currentUser)
|
||||
} else throw new AuthenticationError('Incorrect email address or password.')
|
||||
})
|
||||
session.close()
|
||||
const [currentUser] = await result.records.map(function (record) {
|
||||
return record.get('user')
|
||||
})
|
||||
|
||||
if (
|
||||
currentUser &&
|
||||
(await bcrypt.compareSync(password, currentUser.password))
|
||||
) {
|
||||
delete currentUser.password
|
||||
return encode(currentUser)
|
||||
} else {
|
||||
throw new AuthenticationError('Incorrect email address or password.')
|
||||
}
|
||||
},
|
||||
changePassword: async (
|
||||
_,
|
||||
{ oldPassword, newPassword },
|
||||
{ driver, user }
|
||||
) => {
|
||||
const session = driver.session()
|
||||
let result = await session.run(
|
||||
`MATCH (user:User {email: $userEmail})
|
||||
RETURN user {.id, .email, .password}`,
|
||||
{
|
||||
userEmail: user.email
|
||||
}
|
||||
)
|
||||
|
||||
const [currentUser] = result.records.map(function (record) {
|
||||
return record.get('user')
|
||||
})
|
||||
|
||||
if (!(await bcrypt.compareSync(oldPassword, currentUser.password))) {
|
||||
throw new AuthenticationError('Old password is not correct')
|
||||
}
|
||||
|
||||
if (await bcrypt.compareSync(newPassword, currentUser.password)) {
|
||||
throw new AuthenticationError(
|
||||
'Old password and new password should be different'
|
||||
)
|
||||
} else {
|
||||
const newHashedPassword = await bcrypt.hashSync(newPassword, 10)
|
||||
session.run(
|
||||
`MATCH (user:User {email: $userEmail})
|
||||
SET user.password = $newHashedPassword
|
||||
RETURN user
|
||||
`,
|
||||
{
|
||||
userEmail: user.email,
|
||||
newHashedPassword
|
||||
}
|
||||
)
|
||||
session.close()
|
||||
|
||||
return encode(currentUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,11 +21,14 @@ const factory = Factory()
|
||||
// iss: 'http://localhost:4000',
|
||||
// sub: 'u3'
|
||||
// }
|
||||
const jennyRostocksHeaders = { authorization: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc' }
|
||||
const jennyRostocksHeaders = {
|
||||
authorization:
|
||||
'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoidXNlciIsImxvY2F0aW9uTmFtZSI6bnVsbCwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJhYm91dCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9zMy5hbWF6b25hd3MuY29tL3VpZmFjZXMvZmFjZXMvdHdpdHRlci9zYXNoYV9zaGVzdGFrb3YvMTI4LmpwZyIsImlkIjoidTMiLCJlbWFpbCI6InVzZXJAZXhhbXBsZS5vcmciLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTU1MDg0NjY4MCwiZXhwIjoxNjM3MjQ2NjgwLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.eZ_mVKas4Wzoc_JrQTEWXyRn7eY64cdIg4vqQ-F_7Jc'
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/seyedhossein1/128.jpg',
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||
name: 'Matilde Hermiston',
|
||||
slug: 'matilde-hermiston',
|
||||
@ -43,7 +46,9 @@ describe('isLoggedIn', () => {
|
||||
const query = '{ isLoggedIn }'
|
||||
describe('unauthenticated', () => {
|
||||
it('returns false', async () => {
|
||||
await expect(request(host, query)).resolves.toEqual({ isLoggedIn: false })
|
||||
await expect(request(host, query)).resolves.toEqual({
|
||||
isLoggedIn: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -52,7 +57,9 @@ describe('isLoggedIn', () => {
|
||||
const client = new GraphQLClient(host, { headers })
|
||||
|
||||
it('returns false', async () => {
|
||||
await expect(client.request(query)).resolves.toEqual({ isLoggedIn: false })
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: false
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -60,14 +67,18 @@ describe('isLoggedIn', () => {
|
||||
const client = new GraphQLClient(host, { headers: jennyRostocksHeaders })
|
||||
|
||||
it('returns false', async () => {
|
||||
await expect(client.request(query)).resolves.toEqual({ isLoggedIn: false })
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: false
|
||||
})
|
||||
})
|
||||
|
||||
describe('and a corresponding user in the database', () => {
|
||||
it('returns true', async () => {
|
||||
// see the decoded token above
|
||||
await factory.create('User', { id: 'u3' })
|
||||
await expect(client.request(query)).resolves.toEqual({ isLoggedIn: true })
|
||||
await expect(client.request(query)).resolves.toEqual({
|
||||
isLoggedIn: true
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -116,7 +127,7 @@ describe('currentUser', () => {
|
||||
it('returns the whole user object', async () => {
|
||||
const expected = {
|
||||
currentUser: {
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/seyedhossein1/128.jpg',
|
||||
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
|
||||
email: 'test@example.org',
|
||||
id: 'acb2d923-f3af-479e-9f00-61b12e864666',
|
||||
name: 'Matilde Hermiston',
|
||||
@ -131,7 +142,7 @@ describe('currentUser', () => {
|
||||
})
|
||||
|
||||
describe('login', () => {
|
||||
const mutation = (params) => {
|
||||
const mutation = params => {
|
||||
const { email, password } = params
|
||||
return `
|
||||
mutation {
|
||||
@ -142,10 +153,13 @@ describe('login', () => {
|
||||
describe('ask for a `token`', () => {
|
||||
describe('with valid email/password combination', () => {
|
||||
it('responds with a JWT token', async () => {
|
||||
const data = await request(host, mutation({
|
||||
email: 'test@example.org',
|
||||
password: '1234'
|
||||
}))
|
||||
const data = await request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'test@example.org',
|
||||
password: '1234'
|
||||
})
|
||||
)
|
||||
const token = data.login
|
||||
jwt.verify(token, process.env.JWT_SECRET, (err, data) => {
|
||||
expect(data.email).toEqual('test@example.org')
|
||||
@ -157,10 +171,13 @@ describe('login', () => {
|
||||
describe('with a valid email but incorrect password', () => {
|
||||
it('responds with "Incorrect email address or password."', async () => {
|
||||
await expect(
|
||||
request(host, mutation({
|
||||
email: 'test@example.org',
|
||||
password: 'wrong'
|
||||
}))
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'test@example.org',
|
||||
password: 'wrong'
|
||||
})
|
||||
)
|
||||
).rejects.toThrow('Incorrect email address or password.')
|
||||
})
|
||||
})
|
||||
@ -168,12 +185,89 @@ describe('login', () => {
|
||||
describe('with a non-existing email', () => {
|
||||
it('responds with "Incorrect email address or password."', async () => {
|
||||
await expect(
|
||||
request(host, mutation({
|
||||
email: 'non-existent@example.org',
|
||||
password: 'wrong'
|
||||
}))
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
email: 'non-existent@example.org',
|
||||
password: 'wrong'
|
||||
})
|
||||
)
|
||||
).rejects.toThrow('Incorrect email address or password.')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('change password', () => {
|
||||
let headers
|
||||
let client
|
||||
|
||||
beforeEach(async () => {
|
||||
headers = await login({ email: 'test@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
const mutation = params => {
|
||||
const { oldPassword, newPassword } = params
|
||||
return `
|
||||
mutation {
|
||||
changePassword(oldPassword:"${oldPassword}", newPassword:"${newPassword}")
|
||||
}`
|
||||
}
|
||||
|
||||
describe('should be authenticated before changing password', () => {
|
||||
it('throws not "Not Authorised!', async () => {
|
||||
await expect(
|
||||
request(
|
||||
host,
|
||||
mutation({
|
||||
oldPassword: '1234',
|
||||
newPassword: '1234'
|
||||
})
|
||||
)
|
||||
).rejects.toThrow('Not Authorised!')
|
||||
})
|
||||
})
|
||||
|
||||
describe('old and new password should not match', () => {
|
||||
it('responds with "Old password and new password should be different"', async () => {
|
||||
await expect(
|
||||
client.request(
|
||||
mutation({
|
||||
oldPassword: '1234',
|
||||
newPassword: '1234'
|
||||
})
|
||||
)
|
||||
).rejects.toThrow('Old password and new password should be different')
|
||||
})
|
||||
})
|
||||
|
||||
describe('incorrect old password', () => {
|
||||
it('responds with "Old password isn\'t valid"', async () => {
|
||||
await expect(
|
||||
client.request(
|
||||
mutation({
|
||||
oldPassword: 'notOldPassword',
|
||||
newPassword: '12345'
|
||||
})
|
||||
)
|
||||
).rejects.toThrow('Old password is not correct')
|
||||
})
|
||||
})
|
||||
|
||||
describe('correct password', () => {
|
||||
it('changes the password if given correct credentials "', async () => {
|
||||
let response = await client.request(
|
||||
mutation({
|
||||
oldPassword: '1234',
|
||||
newPassword: '12345'
|
||||
})
|
||||
)
|
||||
await expect(
|
||||
response
|
||||
).toEqual(expect.objectContaining({
|
||||
changePassword: expect.any(String)
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -21,6 +21,7 @@ type Mutation {
|
||||
"Get a JWT Token for the given Email and password"
|
||||
login(email: String!, password: String!): String!
|
||||
signup(email: String!, password: String!): Boolean!
|
||||
changePassword(oldPassword:String!, newPassword: String!): String!
|
||||
report(id: ID!, description: String): Report
|
||||
disable(id: ID!): ID
|
||||
enable(id: ID!): ID
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user