feat((server) users view): add view users grapql query

This commit is contained in:
Gergő Jedlicska
2021-09-14 10:31:30 +02:00
parent 9826fa1676
commit f075b80b0d
5 changed files with 108 additions and 24 deletions
@@ -1,7 +1,7 @@
'use strict'
const appRoot = require( 'app-root-path' )
const { UserInputError } = require( 'apollo-server-express' )
const { getUser, getUserRole, updateUser, deleteUser, searchUsers, getUserById } = require( '../../services/users' )
const { getUser, getUsers, countUsers, getUserRole, updateUser, deleteUser, searchUsers, getUserById } = require( '../../services/users' )
const { saveActivity } = require( `${appRoot}/modules/activitystream/services` )
const { validateServerRole, validateScopes } = require( `${appRoot}/modules/shared` )
const zxcvbn = require( 'zxcvbn' )
@@ -28,6 +28,14 @@ module.exports = {
return await getUser( args.id || context.userId )
},
async users( parent, args, context, info ){
await validateServerRole( context, 'server:admin' )
await validateScopes( context.scopes, 'users:read' )
let users = await getUsers ( args.limit, args.offset )
let totalCount = await countUsers()
return { totalCount, items: users }
},
async userSearch( parent, args, context, info ) {
await validateServerRole( context, 'server:user' )
await validateScopes( context.scopes, 'profile:read' )
@@ -3,6 +3,7 @@ extend type Query {
Gets the profile of a user. If no id argument is provided, will return the current authenticated user's profile (as extracted from the authorization header).
"""
user(id: String): User
users(limit: Int!=25, offset: Int!=0) : UserCollection
userSearch(
query: String!
limit: Int! = 25
@@ -27,6 +28,12 @@ type User {
role: String
}
type UserCollection {
totalCount: Int!
items: [ User ]
}
type UserSearchResultCollection {
cursor: String
items: [UserSearchResult]
+19 -6
View File
@@ -34,12 +34,10 @@ module.exports = {
if ( usr ) throw new Error( 'Email taken. Try logging in?' )
let res = await Users( ).returning( 'id' ).insert( user )
let userRole = parseInt( count ) === 0 ? 'server:admin' : 'server:user'
if ( parseInt( count ) === 0 ) {
await Acl( ).insert( { userId: res[ 0 ], role: 'server:admin' } )
} else {
await Acl( ).insert( { userId: res[ 0 ], role: 'server:user' } )
}
await Acl( ).insert( { userId: res[ 0 ], role: userRole } )
let loggedUser = { ...user }
delete loggedUser.passwordDigest
@@ -60,7 +58,6 @@ module.exports = {
let existingUser = await Users( ).select( 'id' ).where( { email: user.email } ).first( )
if ( existingUser ) {
if ( user.suuid ) {
await module.exports.updateUser( existingUser.id, { suuid: user.suuid } )
}
@@ -140,6 +137,7 @@ module.exports = {
},
async deleteUser( id ) {
//TODO: check for the last admin user to survive
debug( 'speckle:db' )( 'Deleting user ' + id )
let streams = await knex.raw(
`
@@ -167,5 +165,20 @@ module.exports = {
}
return await Users( ).where( { id: id } ).del( )
},
async getUsers ( limit = 10, offset = 0 ) {
// sanitize limit
const maxLimit = 200
if ( limit > maxLimit ) limit = maxLimit
let users =await Users ( ).limit( limit ).offset( offset )
users.map( user => delete user.passwordDigest )
return users
},
async countUsers (){
let [ userCount ] = await Users().count()
return parseInt( userCount.count )
}
}
@@ -11,7 +11,7 @@ chai.use( chaiHttp )
const knex = require( `${appRoot}/db/knex` )
const { createUser, findOrCreateUser, getUser, searchUsers, updateUser, deleteUser, validatePasssword, updateUserPassword } = require( '../services/users' )
const { createUser, findOrCreateUser, getUser, getUsers, searchUsers, countUsers, updateUser, deleteUser, validatePasssword, updateUserPassword, getUserRole } = require( '../services/users' )
const { createPersonalAccessToken, createAppToken, revokeToken, revokeTokenById, validateToken, getUserTokens } = require( '../services/tokens' )
const { grantPermissionsStream, createStream, getStream } = require( '../services/streams' )
@@ -46,7 +46,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
let actorId = await createUser( myTestActor )
myTestActor.id = actorId
} )
after( async ( ) => {
@@ -55,11 +54,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
describe( 'Users @core-users', ( ) => {
it( 'First created user should be a server admin', async ( ) => {
} )
it( 'Should create an user', async ( ) => {
let newUser = { ...myTestActor }
newUser.name = 'Bill Gates'
@@ -83,7 +77,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
} )
it( 'Should not create an user with the same email', async ( ) => {
let newUser = { }
newUser.name = 'Bill Gates'
newUser.email = 'bill@gates.com'
@@ -100,7 +93,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
let ballmerUserId = null
it( 'Find or create should create a user', async ( ) => {
let newUser = { }
newUser.name = 'Steve Ballmer Balls'
newUser.email = 'ballmer@balls.com'
@@ -109,11 +101,9 @@ describe( 'Actors & Tokens @user-services', ( ) => {
let { id } = await findOrCreateUser( { user: newUser } )
ballmerUserId = id
expect( id ).to.be.a( 'string' )
} )
it( 'Find or create should NOT create a user', async ( ) => {
let newUser = { }
newUser.name = 'Steve Ballmer Balls'
newUser.email = 'ballmer@balls.com'
@@ -122,7 +112,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
let { id } = await findOrCreateUser( { user: newUser } )
expect( id ).to.equal( ballmerUserId )
} )
// Note: deletion is more complicated.
@@ -193,7 +182,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
let actor = await getUser( myTestActor.id )
expect( actor.name ).to.equal( updatedActor.name )
} )
it( 'Should not update password', async ( ) => {
@@ -218,7 +206,6 @@ describe( 'Actors & Tokens @user-services', ( ) => {
expect( match ).to.equal( true )
let match_wrong = await validatePasssword( { email: actor.email, password: 'super-test-2000' } )
expect( match_wrong ).to.equal( false )
} )
it( 'Should update the password of a user', async() => {
@@ -285,6 +272,76 @@ describe( 'Actors & Tokens @user-services', ( ) => {
expect( userTokens ).to.have.lengthOf( 2 )
} )
} )
} )
describe ( 'User admin @user-services', ( ) => {
let myTestActor = {
name: 'Gergo Jedlicska',
email: 'gergo@jedlicska.com',
password: 'sn3aky-1337-b1m'
}
before( async () => {
await knex.migrate.rollback( )
await knex.migrate.latest( )
await init()
let actorId = await createUser( myTestActor )
myTestActor.id = actorId
} )
after( async ( ) => {
await knex.migrate.rollback( )
} )
it ( 'First created user should be admin', async () => {
let users = await getUsers( 100, 0 )
expect( users ).to.be.an( 'array' )
expect( users ).to.have.lengthOf( 1 )
let firstUser = users[0]
let userRole = await getUserRole( firstUser.id )
expect( userRole ).to.equal( 'server:admin' )
} )
it ( 'Count user knows how to count', async () => {
expect ( await countUsers() ).to.equal( 1 )
let newUser = { ...myTestActor }
newUser.name = 'Bill Gates'
newUser.email = 'bill@gates.com'
newUser.password = 'testthebest'
let actorId = await createUser( newUser )
expect ( await countUsers() ).to.equal( 2 )
await deleteUser( actorId )
expect ( await countUsers() ).to.equal( 1 )
} )
it ( 'Get users query limit is sanitized to upper limit', async () => {
let createNewDroid = ( number ) => {
return {
name: `${number}`,
email: `${number}@droidarmy.com`,
password: 'sn3aky-1337-b1m'
}
}
let userInputs = Array( 250 ).fill().map( ( v, i ) => createNewDroid( i ) )
expect ( await countUsers() ).to.equal( 1 )
await Promise.all( userInputs.map( userInput => createUser( userInput ) ) )
expect ( await countUsers() ).to.equal( 251 )
let users = await getUsers( 2000000 )
expect ( users ).to.have.lengthOf( 200 )
} )
it ( ' Get users offset is applied', async () => {
let users = await getUsers( 200, 200 )
expect( users ).to.have.lengthOf( 51 )
} )
} )
@@ -21,7 +21,6 @@ module.exports = {
if ( existingUser ) throw new Error( 'This email is already associated with an account on this server!' )
if ( message ) {
if ( message.length >= 1024 ) {
throw new Error( 'Personal message too long.' )
}