Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean identity server #136

Merged
merged 15 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/matrix-client-server/src/utils/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ export class SafeClientEvent {
typeof event.type !== 'string' ||
!isEventTypeValid(event.type)
) {
console.log('Invalid type', event.type)
logger?.error('Invalid type')
throw new Error('Invalid type')
}
Expand Down
13 changes: 11 additions & 2 deletions packages/matrix-identity-server/src/db/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ const tables: Record<Collections, string> = {
shortTermKeypairs:
'keyID varchar(64) PRIMARY KEY, public text, private text, active integer',
userHistory: 'address text PRIMARY KEY, active integer, timestamp integer',
userPolicies: 'user_id text, policy_name text, accepted integer',
userPolicies:
'user_id text, policy_name text, accepted integer, PRIMARY KEY (user_id, policy_name)',
userQuotas: 'user_id varchar(64) PRIMARY KEY, size int'
}

Expand Down Expand Up @@ -557,6 +558,8 @@ class IdentityServerDb<T extends string = never>
.catch((err) => {
/* istanbul ignore next */
this.logger.error('Failed to insert token', err)
/* istanbul ignore next */
reject(err)
})
})
}
Expand Down Expand Up @@ -588,6 +591,7 @@ class IdentityServerDb<T extends string = never>
.catch((err) => {
/* istanbul ignore next */
this.logger.error('Failed to insert token', err)
/* istanbul ignore next */
reject(err)
})
})
Expand All @@ -613,6 +617,7 @@ class IdentityServerDb<T extends string = never>
.catch((e) => {
/* istanbul ignore next */
this.logger.error('Failed to get token', e)
/* istanbul ignore next */
reject(e)
})
})
Expand All @@ -634,12 +639,13 @@ class IdentityServerDb<T extends string = never>
} else {
reject(
new Error(
'Token expired' + (rows[0].expires as number).toString()
'Token expired' + (rows[0]?.expires as number)?.toString()
)
)
}
})
.catch((e) => {
/* istanbul ignore next */
reject(e)
})
})
Expand Down Expand Up @@ -712,6 +718,7 @@ class IdentityServerDb<T extends string = never>
})
})
.catch((e) => {
/* istanbul ignore next */
reject(e)
})
})
Expand Down Expand Up @@ -744,6 +751,8 @@ class IdentityServerDb<T extends string = never>
.catch((err) => {
/* istanbul ignore next */
this.logger.error('Failed to insert ephemeral Key Pair', err)
/* istanbul ignore next */
reject(err)
})
})
}
Expand Down
5 changes: 3 additions & 2 deletions packages/matrix-identity-server/src/db/sql/_createTables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ function createTables<T extends string>(
.then(() => {
_resolve()
})
// istanbul ignore next
.catch((e) => {
/* istanbul ignore next */
_reject(e)
})
} else {
Expand All @@ -77,9 +77,10 @@ function createTables<T extends string>(
.then(() => {
resolve()
})
// istanbul ignore next
.catch((e) => {
/* istanbul ignore next */
logger.error('Unable to create tables', e)
/* istanbul ignore next */
reject(e)
})
}
Expand Down
21 changes: 6 additions & 15 deletions packages/matrix-identity-server/src/db/sql/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,7 @@ class SQLite<T extends string> extends SQL<T> implements IdDbBackend<T> {
}
}
)
stmt.finalize((err) => {
reject(err)
})
stmt.finalize(reject)
})
}

Expand Down Expand Up @@ -150,13 +148,12 @@ class SQLite<T extends string> extends SQL<T> implements IdDbBackend<T> {
}
}
)
stmt.finalize((err) => {
reject(err)
})
stmt.finalize(reject)
})
}

// TODO : Merge update and updateAnd into one function that takes an array of conditions as argument
// Done in Client server - go see updateWithConditions
updateAnd(
table: T,
values: Record<string, string | number>,
Expand All @@ -180,6 +177,7 @@ class SQLite<T extends string> extends SQL<T> implements IdDbBackend<T> {
stmt.all(
vals,
(err: string, rows: Array<Record<string, string | number>>) => {
/* istanbul ignore if */
if (err != null) {
reject(err)
} else {
Expand All @@ -188,12 +186,7 @@ class SQLite<T extends string> extends SQL<T> implements IdDbBackend<T> {
}
)

stmt.finalize((err) => {
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
if (err) {
reject(err)
}
})
stmt.finalize(reject)
})
}

Expand Down Expand Up @@ -794,9 +787,7 @@ class SQLite<T extends string> extends SQL<T> implements IdDbBackend<T> {
resolve()
}
})
stmt.finalize((err) => {
reject(err)
})
stmt.finalize(reject)
})
}

Expand Down
85 changes: 50 additions & 35 deletions packages/matrix-identity-server/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import defaultConfig from './__testData__/registerConf.json'
import IdServer from './index'
import { type Config } from './types'
import { fillPoliciesDB } from './terms/index.post'

jest.mock('node-fetch', () => jest.fn())
const sendMailMock = jest.fn()
jest.mock('nodemailer', () => ({
Expand Down Expand Up @@ -106,6 +107,10 @@ describe('Use configuration file', () => {
idServer.cleanJobs()
})

test('Should have filtered the invalid federated_identity_services', () => {
expect(idServer.conf.federated_identity_services).toEqual([])
})

test('Reject unimplemented endpoint with 404', async () => {
const response = await request(app).get('/_matrix/unknown')
expect(response.statusCode).toBe(404)
Expand Down Expand Up @@ -1369,7 +1374,6 @@ describe('Use configuration file', () => {
room_id: '!room:matrix.org',
sender: '@dwho:matrix.org'
})
console.log(response.body)
expect(response.statusCode).toBe(200)
// TODO : add call to smsMock when it will be implemented
expect(response.body).toHaveProperty('display_name')
Expand All @@ -1381,15 +1385,9 @@ describe('Use configuration file', () => {
})

describe('/_matrix/identity/v2/sign-ed25519 ', () => {
let keyPair: {
publicKey: string
privateKey: string
keyId: string
}
let token: string
let longKeyPair: { publicKey: string; privateKey: string; keyId: string }
beforeAll(async () => {
keyPair = generateKeyPair('ed25519')
longKeyPair = generateKeyPair('ed25519')
await idServer.db.insert('longTermKeypairs', {
name: 'currentKey',
Expand Down Expand Up @@ -1438,7 +1436,7 @@ describe('Use configuration file', () => {
.set('Accept', 'application/json')
.send({
mxid: 'invalid_mxid',
private_key: keyPair.privateKey,
private_key: longKeyPair.privateKey,
token
})
expect(response.statusCode).toBe(400)
Expand All @@ -1450,7 +1448,7 @@ describe('Use configuration file', () => {
.set('Accept', 'application/json')
.send({
mxid: '@test:matrix.org',
private_key: keyPair.privateKey,
private_key: longKeyPair.privateKey,
token: ''
})
expect(response.statusCode).toBe(400)
Expand All @@ -1462,7 +1460,7 @@ describe('Use configuration file', () => {
.set('Accept', 'application/json')
.send({
mxid: '@test:matrix.org',
private_key: keyPair.privateKey,
private_key: longKeyPair.privateKey,
token: 'invalidtoken'
})
expect(response.statusCode).toBe(404)
Expand All @@ -1474,7 +1472,7 @@ describe('Use configuration file', () => {
.set('Accept', 'application/json')
.send({
mxid: '@test:matrix.org',
private_key: keyPair.privateKey,
private_key: longKeyPair.privateKey,
token
})
expect(response.statusCode).toBe(200)
Expand Down Expand Up @@ -1619,7 +1617,7 @@ describe('_matrix/identity/v2/terms', () => {
let conf2: Config
let app2: express.Application
let validToken2: string
let userId: string
const userId = '@dwho:example.com'
const policies = {
privacy_policy: {
en: {
Expand Down Expand Up @@ -1668,11 +1666,8 @@ describe('_matrix/identity/v2/terms', () => {
done(e)
})
})

afterAll(() => {
idServer2.cleanJobs()
})
it('copy of register test', async () => {
beforeAll(async () => {
idServer2.logger.info('Calling register to obtain a valid token')
const mockResponse = Promise.resolve({
ok: true,
status: 200,
Expand All @@ -1696,37 +1691,57 @@ describe('_matrix/identity/v2/terms', () => {
})
.set('Accept', 'application/json')
expect(response.statusCode).toBe(200)
expect(response.body.token).toMatch(/^[a-zA-Z0-9]{64}$/)
validToken2 = response.body.token

idServer2.logger.info('Adding the policies for the user in the db')
try {
fillPoliciesDB(userId, idServer2, 0)
idServer2.logger.info('Successfully added policies for the user')
} catch (e) {
idServer2.logger.error('Error while setting up policies for the user', e)
}
})

afterAll(async () => {
idServer2.cleanJobs()
})

it('should update policies', async () => {
const rows = await idServer2.db.get('accessTokens', ['data'], {
id: validToken2
})
userId = JSON.parse(rows[0].data as string).sub
await idServer2.db.insert('userPolicies', {
policy_name: 'terms_of_service 2.0',
user_id: userId,
accepted: 0
})
const response2 = await request(app2)
const response = await request(app2)
.post('/_matrix/identity/v2/terms')
.set('Accept', 'application/json')
.set('Authorization', `Bearer ${validToken2}`)
.send({ user_accepts: policies.terms_of_service.en.url })
expect(response2.statusCode).toBe(200)
const response3 = await idServer2.db.get('userPolicies', ['accepted'], {
.send({ user_accepts: policies.privacy_policy.en.url })
expect(response.statusCode).toBe(200)
const response2 = await idServer2.db.get('userPolicies', ['accepted'], {
user_id: userId,
policy_name: 'terms_of_service 2.0'
policy_name: 'privacy_policy 1.2'
})
expect(response3[0].accepted).toBe(1)
expect(response2[0].accepted).toBe(1)
})
it('should refuse authentifying a user that did not accept the terms', async () => {
fillPoliciesDB(userId, idServer2, 0)
it('should refuse authentifying a user who did not accept the terms', async () => {
const response = await request(app2)
.get('/_matrix/identity/v2/account')
.set('Authorization', `Bearer ${validToken2}`)
.set('Accept', 'application/json')
expect(response.statusCode).toBe(403)
})
describe('After accepting the terms', () => {
beforeAll(async () => {
idServer2.logger.info('Accepting the policies for the user in the db')
try {
fillPoliciesDB(userId, idServer2, 1)
idServer2.logger.info('Successfully accepted policies for the user')
} catch (e) {
idServer2.logger.error('Error while accepting policies for the user', e)
}
})
it('should accept authentifying a user who accepted the terms', async () => {
const response = await request(app2)
.get('/_matrix/identity/v2/account')
.set('Authorization', `Bearer ${validToken2}`)
.set('Accept', 'application/json')
expect(response.statusCode).toBe(200)
})
})
})
21 changes: 12 additions & 9 deletions packages/matrix-identity-server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import defaultConfDesc from './config.json'
import CronTasks from './cron'
import {
errMsg as _errMsg,
hostnameRe,
isHostnameValid,
send,
type expressAppHandler
} from '@twake/utils'
Expand Down Expand Up @@ -122,14 +122,17 @@ export default class MatrixIdentityServer<T extends string = never> {
? '/etc/twake/identity-server.conf'
: undefined
) as Config
this.conf.federated_identity_services =
typeof this.conf.federated_identity_services === 'object'
? this.conf.federated_identity_services
: typeof this.conf.federated_identity_services === 'string'
? (this.conf.federated_identity_services as string)
.split(/[,\s]+/)
.filter((addr) => addr.match(hostnameRe))
: []
this.conf.federated_identity_services = Array.isArray(
this.conf.federated_identity_services
)
? this.conf.federated_identity_services.filter((addr) =>
isHostnameValid(addr)
)
: typeof this.conf.federated_identity_services === 'string'
? (this.conf.federated_identity_services as string)
.split(/[,\s]+/)
.filter((addr) => isHostnameValid(addr))
: []
this._convertStringtoNumberInConfig()
this.rateLimiter = rateLimit({
windowMs: this.conf.rate_limiting_window,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ import type IdentityServerDB from '../db'
import { errMsg, send, type expressAppHandler } from '@twake/utils'

const getPubkey = <T extends string = never>(
idServer: IdentityServerDB<T>
idServerDB: IdentityServerDB<T>
): expressAppHandler => {
return (req, res) => {
const _keyID: string = (req as Request).params.keyId

idServer.db
idServerDB
.get('shortTermKeypairs', ['public'], { keyID: _keyID })
// eslint-disable-next-line @typescript-eslint/promise-function-async
.then((rows) => {
if (rows.length === 1) {
send(res, 200, { public_key: rows[0].public })
} else {
return idServer.db
return idServerDB
.get('longTermKeypairs', ['public'], { keyID: _keyID })
.then((rows) => {
if (rows.length === 0) {
Expand Down
Loading
Loading