forked from specklesystems/speckle-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
159 additions
and
133 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,74 +1,88 @@ | ||
'use strict' | ||
const appRoot = require( 'app-root-path' ) | ||
const { ApolloError, AuthenticationError, UserInputError } = require( 'apollo-server-express' ) | ||
const { createUser, getUser, getUserByEmail, getUserRole, updateUser, deleteUser, validatePasssword } = require( '../../services/users' ) | ||
const { createPersonalAccessToken, createAppToken, revokeToken, revokeTokenById, validateToken, getUserTokens } = require( '../../services/tokens' ) | ||
const { validateServerRole, validateScopes, authorizeResolver } = require( `${appRoot}/modules/shared` ) | ||
const setupCheck = require( `${appRoot}/setupcheck` ) | ||
const zxcvbn = require( 'zxcvbn' ) | ||
const appRoot = require('app-root-path') | ||
const { ApolloError, AuthenticationError, UserInputError } = require('apollo-server-express') | ||
const { createUser, getUser, getUserByEmail, getUserRole, updateUser, deleteUser, findUsers, validatePasssword } = require('../../services/users') | ||
const { createPersonalAccessToken, createAppToken, revokeToken, revokeTokenById, validateToken, getUserTokens } = require('../../services/tokens') | ||
const { validateServerRole, validateScopes, authorizeResolver } = require(`${appRoot}/modules/shared`) | ||
const setupCheck = require(`${appRoot}/setupcheck`) | ||
const zxcvbn = require('zxcvbn') | ||
|
||
module.exports = { | ||
Query: { | ||
Query: { | ||
|
||
async _( ) { | ||
return `Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn.` | ||
}, | ||
async _() { | ||
return `Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn.` | ||
}, | ||
|
||
async user(parent, args, context, info) { | ||
|
||
await validateServerRole(context, 'server:user') | ||
|
||
if (!args.id) | ||
await validateScopes(context.scopes, 'profile:read') | ||
else | ||
await validateScopes(context.scopes, 'users:read') | ||
|
||
if (!args.id && !context.userId) { | ||
throw new UserInputError('You must provide an user id.') | ||
} | ||
|
||
return await getUser(args.id || context.userId) | ||
}, | ||
|
||
async user( parent, args, context, info ) { | ||
async users(parent, args, context, info) { | ||
|
||
await validateServerRole( context, 'server:user' ) | ||
await validateServerRole(context, 'server:user') | ||
|
||
if ( !args.id ) | ||
await validateScopes( context.scopes, 'profile:read' ) | ||
else | ||
await validateScopes( context.scopes, 'users:read' ) | ||
if (!args.id) | ||
await validateScopes(context.scopes, 'profile:read') | ||
else | ||
await validateScopes(context.scopes, 'users:read') | ||
|
||
if ( !args.id && !context.userId ) { | ||
throw new UserInputError( 'You must provide an user id.' ) | ||
} | ||
return await findUsers(args.query) | ||
}, | ||
|
||
async userPwdStrength(parent, args, context, info) { | ||
let res = zxcvbn(args.pwd) | ||
return { score: res.score, feedback: res.feedback } | ||
} | ||
|
||
return await getUser( args.id || context.userId ) | ||
}, | ||
|
||
async userPwdStrength( parent, args, context, info ) { | ||
let res = zxcvbn( args.pwd ) | ||
return { score: res.score, feedback: res.feedback } | ||
} | ||
User: { | ||
|
||
}, | ||
async email(parent, args, context, info) { | ||
// NOTE: we're redacting the field (returning null) rather than throwing a full error which would invalidate the request. | ||
if (context.userId === parent.id) { | ||
try { | ||
await validateScopes(context.scopes, 'profile:email') | ||
return parent.email | ||
} catch (err) { | ||
return null | ||
} | ||
} | ||
|
||
User: { | ||
try { | ||
await validateScopes(context.scopes, 'users:email') | ||
return parent.email | ||
} catch (err) { | ||
return null | ||
} | ||
}, | ||
|
||
async email( parent, args, context, info ) { | ||
// NOTE: we're redacting the field (returning null) rather than throwing a full error which would invalidate the request. | ||
if ( context.userId === parent.id ) { | ||
try { | ||
await validateScopes( context.scopes, 'profile:email' ) | ||
return parent.email | ||
} catch ( err ) { | ||
return null | ||
async role(parent, args, context, info) { | ||
return await getUserRole(parent.id) | ||
} | ||
} | ||
|
||
try { | ||
await validateScopes( context.scopes, 'users:email' ) | ||
return parent.email | ||
} catch ( err ) { | ||
return null | ||
} | ||
|
||
}, | ||
|
||
async role( parent, args, context, info ) { | ||
return await getUserRole( parent.id ) | ||
} | ||
|
||
}, | ||
|
||
Mutation: { | ||
async userEdit( parent, args, context, info ) { | ||
await validateServerRole( context, 'server:user' ) | ||
await updateUser( context.userId, args.user ) | ||
return true | ||
Mutation: { | ||
async userEdit(parent, args, context, info) { | ||
await validateServerRole(context, 'server:user') | ||
await updateUser(context.userId, args.user) | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,93 +1,104 @@ | ||
'use strict' | ||
const bcrypt = require( 'bcrypt' ) | ||
const crs = require( 'crypto-random-string' ) | ||
const appRoot = require( 'app-root-path' ) | ||
const knex = require( `${appRoot}/db/knex` ) | ||
const bcrypt = require('bcrypt') | ||
const crs = require('crypto-random-string') | ||
const appRoot = require('app-root-path') | ||
const knex = require(`${appRoot}/db/knex`) | ||
|
||
const Users = ( ) => knex( 'users' ) | ||
const ServerRoles = ( ) => knex( 'server_acl' ) | ||
const Users = () => knex('users') | ||
const ServerRoles = () => knex('server_acl') | ||
|
||
module.exports = { | ||
|
||
/* | ||
/* | ||
Users | ||
Users | ||
*/ | ||
*/ | ||
|
||
async createUser( user ) { | ||
let [ { count } ] = await ServerRoles( ).where( { role: 'server:admin' } ).count( ) | ||
async createUser(user) { | ||
let [{ count }] = await ServerRoles().where({ role: 'server:admin' }).count() | ||
|
||
user.id = crs( { length: 10 } ) | ||
user.id = crs({ length: 10 }) | ||
|
||
if ( user.password ) { | ||
user.passwordDigest = await bcrypt.hash( user.password, 10 ) | ||
} | ||
delete user.password | ||
if (user.password) { | ||
user.passwordDigest = await bcrypt.hash(user.password, 10) | ||
} | ||
delete user.password | ||
|
||
let usr = await Users( ).select( 'id' ).where( { email: user.email } ).first( ) | ||
if ( usr ) throw new Error( 'Email taken. Try logging in?' ) | ||
let usr = await Users().select('id').where({ email: user.email }).first() | ||
if (usr) throw new Error('Email taken. Try logging in?') | ||
|
||
let res = await Users( ).returning( 'id' ).insert( user ) | ||
let res = await Users().returning('id').insert(user) | ||
|
||
if ( parseInt( count ) === 0 ) { | ||
await ServerRoles( ).insert( { userId: res[ 0 ], role: 'server:admin' } ) | ||
} else { | ||
await ServerRoles( ).insert( { userId: res[ 0 ], role: 'server:user' } ) | ||
} | ||
if (parseInt(count) === 0) { | ||
await ServerRoles().insert({ userId: res[0], role: 'server:admin' }) | ||
} else { | ||
await ServerRoles().insert({ userId: res[0], role: 'server:user' }) | ||
} | ||
|
||
return res[0] | ||
}, | ||
|
||
async findOrCreateUser({ user, rawProfile }) { | ||
let existingUser = await Users().select('id').where({ email: user.email }).first() | ||
|
||
if (existingUser) | ||
return existingUser | ||
|
||
user.password = crs({ length: 20 }) | ||
user.verified = true // because we trust the external identity provider, no? | ||
return { id: await module.exports.createUser(user) } | ||
}, | ||
|
||
async getUserById({ userId }) { | ||
let user = await Users().where({ id: userId }).select('*').first() | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
// TODO: deprecate | ||
async getUser(id) { | ||
let user = await Users().where({ id: id }).select('*').first() | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
return res[ 0 ] | ||
}, | ||
|
||
async findOrCreateUser( { user, rawProfile } ) { | ||
let existingUser = await Users( ).select( 'id' ).where( { email: user.email } ).first( ) | ||
|
||
if ( existingUser ) | ||
return existingUser | ||
|
||
user.password = crs( { length: 20 } ) | ||
user.verified = true // because we trust the external identity provider, no? | ||
return { id: await module.exports.createUser( user ) } | ||
}, | ||
|
||
async getUserById( { userId } ) { | ||
let user = await Users( ).where( { id: userId } ).select( '*' ).first( ) | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
// TODO: deprecate | ||
async getUser( id ) { | ||
let user = await Users( ).where( { id: id } ).select( '*' ).first( ) | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
async getUserByEmail( { email } ) { | ||
let user = await Users( ).where( { email: email } ).select( '*' ).first( ) | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
async getUserRole( id ) { | ||
let { role } = await ServerRoles( ).where( { userId: id } ).select( 'role' ).first( ) | ||
return role | ||
}, | ||
|
||
async updateUser( id, user ) { | ||
delete user.id | ||
delete user.passwordDigest | ||
delete user.password | ||
delete user.email | ||
await Users( ).where( { id: id } ).update( user ) | ||
}, | ||
|
||
async validatePasssword( { email, password } ) { | ||
let { passwordDigest } = await Users( ).where( { email: email } ).select( 'passwordDigest' ).first( ) | ||
return bcrypt.compare( password, passwordDigest ) | ||
}, | ||
|
||
async deleteUser( id ) { | ||
throw new Error( 'not implemented' ) | ||
} | ||
} | ||
async getUserByEmail({ email }) { | ||
let user = await Users().where({ email: email }).select('*').first() | ||
delete user.passwordDigest | ||
return user | ||
}, | ||
|
||
async getUserRole(id) { | ||
let { role } = await ServerRoles().where({ userId: id }).select('role').first() | ||
return role | ||
}, | ||
|
||
async updateUser(id, user) { | ||
delete user.id | ||
delete user.passwordDigest | ||
delete user.password | ||
delete user.email | ||
await Users().where({ id: id }).update(user) | ||
}, | ||
|
||
async findUsers(query) { | ||
|
||
query = "%" + query + "%"; | ||
let users = await Users() | ||
.where('email', 'like', query) | ||
.orWhere('username', 'like', query) | ||
.orWhere('name', 'like', query) | ||
|
||
return users | ||
}, | ||
|
||
async validatePasssword({ email, password }) { | ||
let { passwordDigest } = await Users().where({ email: email }).select('passwordDigest').first() | ||
return bcrypt.compare(password, passwordDigest) | ||
}, | ||
|
||
async deleteUser(id) { | ||
throw new Error('not implemented') | ||
} | ||
} |