feat((server) users view): add view users grapql query
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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.' )
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user