311 lines
9.6 KiB
TypeScript
311 lines
9.6 KiB
TypeScript
/* eslint-disable camelcase */
|
|
/* istanbul ignore file */
|
|
import chai from 'chai'
|
|
|
|
const expect = chai.expect
|
|
|
|
import {
|
|
createBareToken,
|
|
createAppTokenFactory,
|
|
createPersonalAccessTokenFactory
|
|
} from '@/modules/core/services/tokens'
|
|
import { beforeEachContext, initializeTestServer } from '@/test/hooks'
|
|
import { Scopes } from '@speckle/shared'
|
|
import {
|
|
createAuthorizationCodeFactory,
|
|
getAuthorizationCodeFactory,
|
|
deleteAuthorizationCodeFactory,
|
|
getAppFactory,
|
|
createRefreshTokenFactory
|
|
} from '@/modules/auth/repositories/apps'
|
|
import { db } from '@/db/knex'
|
|
import { createAppTokenFromAccessCodeFactory } from '@/modules/auth/services/serverApps'
|
|
import {
|
|
storeApiTokenFactory,
|
|
storeTokenScopesFactory,
|
|
storeTokenResourceAccessDefinitionsFactory,
|
|
storeUserServerAppTokenFactory,
|
|
storePersonalApiTokenFactory
|
|
} from '@/modules/core/repositories/tokens'
|
|
import { createTestUser, type BasicTestUser } from '@/test/authHelper'
|
|
|
|
let sendRequest: Awaited<ReturnType<typeof initializeTestServer>>['sendRequest']
|
|
|
|
const createAppToken = createAppTokenFactory({
|
|
storeApiToken: storeApiTokenFactory({ db }),
|
|
storeTokenScopes: storeTokenScopesFactory({ db }),
|
|
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
|
|
db
|
|
}),
|
|
storeUserServerAppToken: storeUserServerAppTokenFactory({ db })
|
|
})
|
|
const createAuthorizationCode = createAuthorizationCodeFactory({ db })
|
|
const createAppTokenFromAccessCode = createAppTokenFromAccessCodeFactory({
|
|
getAuthorizationCode: getAuthorizationCodeFactory({ db }),
|
|
deleteAuthorizationCode: deleteAuthorizationCodeFactory({ db }),
|
|
getApp: getAppFactory({ db }),
|
|
createRefreshToken: createRefreshTokenFactory({ db }),
|
|
createAppToken,
|
|
createBareToken
|
|
})
|
|
|
|
const createPersonalAccessToken = createPersonalAccessTokenFactory({
|
|
storeApiToken: storeApiTokenFactory({ db }),
|
|
storeTokenScopes: storeTokenScopesFactory({ db }),
|
|
storeTokenResourceAccessDefinitions: storeTokenResourceAccessDefinitionsFactory({
|
|
db
|
|
}),
|
|
storePersonalApiToken: storePersonalApiTokenFactory({ db })
|
|
})
|
|
|
|
describe('GraphQL @apps-api', () => {
|
|
let testUser: BasicTestUser
|
|
let testUser2: BasicTestUser
|
|
let testToken: string
|
|
let testToken2: string
|
|
|
|
before(async () => {
|
|
const ctx = await beforeEachContext()
|
|
;({ sendRequest } = await initializeTestServer(ctx))
|
|
|
|
testUser = await createTestUser({
|
|
name: 'Dimitrie Stefanescu',
|
|
email: 'didimitrie@example.org',
|
|
password: 'wtfwtfwtf',
|
|
id: ''
|
|
})
|
|
testToken = `Bearer ${await createPersonalAccessToken(testUser.id, 'test token', [
|
|
Scopes.Profile.Read,
|
|
Scopes.Apps.Read,
|
|
Scopes.Apps.Write
|
|
])}`
|
|
|
|
testUser2 = await createTestUser({
|
|
name: 'Mr. Mac',
|
|
email: 'steve@jobs.com',
|
|
password: 'wtfwtfwtf',
|
|
id: ''
|
|
})
|
|
testToken2 = `Bearer ${await createPersonalAccessToken(testUser2.id, 'test token', [
|
|
Scopes.Profile.Read,
|
|
Scopes.Apps.Read,
|
|
Scopes.Apps.Write
|
|
])}`
|
|
})
|
|
|
|
let testAppId: string
|
|
let testApp: { secret: string }
|
|
|
|
it('Should create an app', async () => {
|
|
const query =
|
|
'mutation createApp($myApp:AppCreateInput!) { appCreate( app: $myApp ) } '
|
|
const variables = {
|
|
myApp: {
|
|
name: 'Test App',
|
|
public: true,
|
|
description: 'Test App Description',
|
|
scopes: [Scopes.Streams.Read],
|
|
redirectUrl: 'lol://what'
|
|
}
|
|
}
|
|
|
|
const res = await sendRequest(testToken, { query, variables })
|
|
expect(res).to.be.json
|
|
expect(res.body.errors).to.not.exist
|
|
testAppId = res.body.data.appCreate
|
|
})
|
|
|
|
it('Should not create an app if request is not authenticated', async () => {
|
|
const query = `
|
|
mutation createApp($myApp:AppCreateInput!) {
|
|
appCreate( app: $myApp )
|
|
}
|
|
`
|
|
const variables = {
|
|
myApp: {
|
|
name: 'Test App',
|
|
description: 'Test App Description',
|
|
scopes: [Scopes.Streams.Read],
|
|
redirectUrl: 'lol://what'
|
|
}
|
|
}
|
|
|
|
const res = await sendRequest(null, { query, variables })
|
|
expect(res).to.be.json
|
|
expect(res.body.errors).to.exist
|
|
expect(res.body.errors[0].extensions?.code).to.equal('UNAUTHORIZED')
|
|
})
|
|
|
|
it('Should get app info', async () => {
|
|
const query = `
|
|
query getApp {
|
|
app( id: "${testAppId}") {
|
|
name
|
|
secret
|
|
description
|
|
author {
|
|
name
|
|
id
|
|
}
|
|
scopes {
|
|
name
|
|
description
|
|
}
|
|
}
|
|
}
|
|
`
|
|
|
|
const res = await sendRequest(testToken, { query })
|
|
expect(res).to.be.json
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.app.name).to.equal('Test App')
|
|
expect(res.body.data.app.scopes.length).to.equal(1)
|
|
expect(res.body.data.app.secret).to.exist
|
|
testApp = res.body.data.app
|
|
})
|
|
|
|
it('Should get all the public apps on this server', async () => {
|
|
const query = 'query allapps{ apps { name description author { id name } } }'
|
|
const res = await sendRequest(null, { query })
|
|
expect(res).to.be.json
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.apps).to.be.an('array')
|
|
expect(res.body.data.apps.length).to.equal(10)
|
|
})
|
|
|
|
it('Should get app info without secret if not authenticated and owner', async () => {
|
|
const query = `query getApp { app( id: "${testAppId}") { name secret } }`
|
|
|
|
const res = await sendRequest(null, { query })
|
|
expect(res.body.data.app.secret).to.equal(
|
|
'App secrets are only revealed to their author 😉'
|
|
)
|
|
|
|
const res2 = await sendRequest(testToken2, { query })
|
|
expect(res2.body.data.app.secret).to.equal(
|
|
'App secrets are only revealed to their author 😉'
|
|
)
|
|
})
|
|
|
|
it('Should update app info', async () => {
|
|
const query = `
|
|
mutation updateApp($myApp:AppUpdateInput!) {
|
|
appUpdate( app: $myApp )
|
|
}
|
|
`
|
|
const variables = {
|
|
myApp: {
|
|
id: testAppId,
|
|
name: 'Updated Test App',
|
|
description: 'Test App Description',
|
|
scopes: [Scopes.Streams.Read],
|
|
redirectUrl: 'lol://what'
|
|
}
|
|
}
|
|
|
|
const res = await sendRequest(testToken, { query, variables })
|
|
expect(res).to.be.json
|
|
expect(res.body.data.appUpdate).to.equal(true)
|
|
|
|
const query2 = `query getApp { app( id: "${testAppId}") { name } }`
|
|
const res2 = await sendRequest(null, { query: query2 })
|
|
|
|
expect(res2.body.data.app.name).to.equal('Updated Test App')
|
|
})
|
|
|
|
it('Should not delete app if request is not authenticated/user is app owner', async () => {
|
|
const query = `mutation del { appDelete( appId: "${testAppId}" ) }`
|
|
const res = await sendRequest(null, { query })
|
|
expect(res.body.errors).to.exist
|
|
expect(res.body.errors[0].extensions?.code).to.equal('UNAUTHORIZED')
|
|
|
|
const res2 = await sendRequest(testToken2, { query })
|
|
expect(res2.body.errors).to.exist
|
|
expect(res2.body.errors[0].extensions?.code).to.equal('FORBIDDEN')
|
|
})
|
|
|
|
it('Should get the apps that i have created', async () => {
|
|
const query =
|
|
'mutation createApp($myApp:AppCreateInput!) { appCreate( app: $myApp ) } '
|
|
let variables = {
|
|
myApp: {
|
|
name: 'Another Test App',
|
|
public: false,
|
|
description: 'Test App Description',
|
|
scopes: [Scopes.Streams.Read],
|
|
redirectUrl: 'lol://what'
|
|
}
|
|
}
|
|
await sendRequest(testToken, { query, variables })
|
|
|
|
variables = {
|
|
myApp: {
|
|
name: 'The n-th Test App',
|
|
public: false,
|
|
description: 'Test App Description',
|
|
scopes: [Scopes.Streams.Read],
|
|
redirectUrl: 'lol://what'
|
|
}
|
|
}
|
|
await sendRequest(testToken, { query, variables })
|
|
|
|
const getMyAppsQuery =
|
|
'query usersApps{ user { createdApps { id name description } } }'
|
|
|
|
const res = await sendRequest(testToken, { query: getMyAppsQuery })
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.user.createdApps).to.be.an('array')
|
|
expect(res.body.data.user.createdApps.length).to.equal(3)
|
|
})
|
|
|
|
it('Should get my authorised apps', async () => {
|
|
// 'authorize' the test app.
|
|
const authorizationCode_1 = await createAuthorizationCode({
|
|
appId: testAppId,
|
|
userId: testUser.id,
|
|
challenge: 'floating points'
|
|
})
|
|
await createAppTokenFromAccessCode({
|
|
appId: testAppId,
|
|
appSecret: testApp.secret,
|
|
accessCode: authorizationCode_1,
|
|
challenge: 'floating points'
|
|
})
|
|
|
|
const authorizationCode_2 = await createAuthorizationCode({
|
|
appId: 'sdm',
|
|
userId: testUser.id,
|
|
challenge: 'floating points'
|
|
})
|
|
await createAppTokenFromAccessCode({
|
|
appId: 'sdm',
|
|
appSecret: 'sdm',
|
|
accessCode: authorizationCode_2,
|
|
challenge: 'floating points'
|
|
})
|
|
|
|
const query =
|
|
'query myAuthApps{ user { authorizedApps { id name description termsAndConditionsLink logo author { id name } } } }'
|
|
|
|
const res = await sendRequest(testToken, { query })
|
|
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.user.authorizedApps).to.be.an('array')
|
|
expect(res.body.data.user.authorizedApps.length).to.equal(2)
|
|
})
|
|
|
|
it('Should revoke access to an app I have authorised', async () => {
|
|
const query = `mutation revokeAcces{ appRevokeAccess( appId: "${testAppId}" ) }`
|
|
const res = await sendRequest(testToken, { query })
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.appRevokeAccess).to.equal(true)
|
|
})
|
|
|
|
it('Should delete app', async () => {
|
|
const query = `mutation del { appDelete( appId: "${testAppId}" ) }`
|
|
const res = await sendRequest(testToken, { query })
|
|
expect(res.body.errors).to.not.exist
|
|
expect(res.body.data.appDelete).to.equal(true)
|
|
})
|
|
})
|