Skip to content

Commit

Permalink
Merge pull request #26 from clevertech/update/dependencies
Browse files Browse the repository at this point in the history
Update/dependencies
  • Loading branch information
jbmikk authored Nov 12, 2020
2 parents f43b9d5 + 9b127a1 commit 7819c96
Show file tree
Hide file tree
Showing 10 changed files with 2,661 additions and 6,641 deletions.
15 changes: 15 additions & 0 deletions core/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@

# CleverAuth Core

Generic auth library for Clevertech.


## Testing

You have to run the tests with node `v10` or `v12`. Node `v14` has a known issue with older `pg`
clients.

docker-compose -f test/docker-compose.yml up -d
npm run test


## Example

```javascript
import {
Core,
Expand Down
31 changes: 15 additions & 16 deletions core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@clevertech.biz/auth-core",
"version": "1.0.2",
"version": "2.0.0",
"description": "The set of utilities necessary to build an authentication service. This does not include templates, views, styles, or transport layer.",
"keywords": [],
"main": "dist/library.js",
Expand Down Expand Up @@ -76,50 +76,49 @@
"collectCoverage": true,
"coverageThreshold": {
"global": {
"branches": 90,
"functions": 95,
"lines": 95,
"statements": 95
"branches": 60,
"functions": 60,
"lines": 70,
"statements": 65
}
}
},
"devDependencies": {
"@types/jest": "^22.0.0",
"@types/node": "^8.0.0",
"@types/jest": "^25.1.0",
"@types/node": "^10.17.0",
"commitizen": "^2.9.6",
"coveralls": "^2.13.1",
"cz-conventional-changelog": "^2.0.0",
"husky": "^0.14.0",
"jest": "^22.0.0",
"jest": "^25.1.0",
"lint-staged": "^4.0.0",
"mysql": "^2.15.0",
"pg": "^7.4.0",
"prettier": "^1.9.2",
"rimraf": "^2.6.1",
"semantic-release": "^8.0.0",
"ts-jest": "^22.0.0",
"ts-node": "^3.0.6",
"ts-jest": "^25.1.0",
"ts-node": "^8.9.1",
"tslint": "^5.4.3",
"tslint-config-prettier": "^1.1.0",
"tslint-config-standard": "^6.0.0",
"typedoc": "^0.8.0",
"typescript": "^2.3.4",
"typedoc": "0.16.11",
"typescript": "^3.7.5",
"validate-commit-msg": "^2.12.2"
},
"dependencies": {
"@types/joi": "^10.4.3",
"@types/jsonwebtoken": "^7.2.3",
"@types/knex": "^0.0.61",
"@types/lodash": "^4.14.76",
"@types/lodash": "^4.14.165",
"@types/mongodb": "^2.2.11",
"@types/node-fetch": "^1.6.7",
"@types/qrcode": "^0.8.0",
"@types/speakeasy": "^2.0.1",
"@types/uuid": "^3.4.2",
"joi": "^11.1.1",
"jsonwebtoken": "^8.0.1",
"knex": "^0.13.0",
"lodash": "^4.17.4",
"knex": "^0.20.15",
"lodash": "^4.17.20",
"mongodb": "^2.2.31",
"node-fetch": "^1.7.3",
"pnp-email-service": "^0.1.8",
Expand Down
5 changes: 3 additions & 2 deletions core/src/database/knex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export default class KnexAdapter implements IDatabaseAdapter {
table
.text('emailConfirmationToken')
.nullable()
.unique()
// Mysql does not support unique on blobs
//.unique()
table.string('termsAndConditions').nullable()
table.timestamps()
})
Expand Down Expand Up @@ -186,7 +187,7 @@ export default class KnexAdapter implements IDatabaseAdapter {
}

async insertUser(user: IUser): Promise<string> {
user = omit(user, ['id', '_id'])
user = omit(user, ['id', '_id']) as IUser
const userId = uuid()
user.id = userId
return this.db('auth_users')
Expand Down
14 changes: 13 additions & 1 deletion core/src/database/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import * as mongo from 'mongodb';
import { IProvider, IRecoveryCode, IUser, IUserUpdate } from '../types';
import { IDatabaseAdapter } from './adapter';

const UNINITIALIZED_ADAPTER = 'Mongo adapter was not initialized';

export default class MongoAdapter implements IDatabaseAdapter {
private databaseURL: string
private db: mongo.Db
private db: mongo.Db | undefined

constructor(databaseURL: string) {
this.databaseURL = databaseURL
Expand All @@ -22,16 +24,19 @@ export default class MongoAdapter implements IDatabaseAdapter {
}

public async findUserByEmail(email: string): Promise<IUser | undefined> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
return this.normalize(await this.db.collection('auth_users').findOne({ email }))
}

public async findUserById(id: string): Promise<IUser | undefined> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
return this.normalize(
await this.db.collection('auth_users').findOne({ _id: new mongo.ObjectID(id) })
)
}

public async findUserByProviderLogin(login: string): Promise<IUser | undefined> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
const provider = await this.db.collection('auth_providers').findOne({ login })
if (!provider) {
return undefined
Expand All @@ -42,43 +47,50 @@ export default class MongoAdapter implements IDatabaseAdapter {
}

public findRecoveryCodesByUserId(userId: string): Promise<IRecoveryCode[]> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
return this.db
.collection('auth_recovery_codes')
.find({ userId })
.toArray()
}

public async insertRecoveryCodes(userId: string, codes: string[]): Promise<IRecoveryCode[]> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
await this.db.collection('auth_recovery_codes').deleteMany({ userId })

await Promise.all(
codes.map(code => {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
return this.db.collection('auth_recovery_codes').insertOne({ userId, code, used: false })
})
)
return codes.map(code => ({ code, used: false }))
}

public async useRecoveryCode(userId: string, code: string): Promise<boolean> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
const res = await this.db
.collection('auth_recovery_codes')
.updateOne({ userId, code: code.toLowerCase(), used: false }, { $set: { used: true } })
return !!res.result.nModified
}

public async insertUser(user: IUser): Promise<string> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
const res = await this.db.collection('auth_users').insertOne(user)
return res.insertedId.toHexString()
}

public async updateUser(user: IUserUpdate): Promise<void> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
const res = await this.db
.collection('auth_users')
.update({ _id: new mongo.ObjectID(user.id!) }, { $set: omit(user, 'id') })
return res.result.nModified
}

public async insertProvider(provider: IProvider): Promise<void> {
if(!this.db) throw new Error(UNINITIALIZED_ADAPTER);
await this.db.collection('auth_providers').insertOne(provider)
// return res.insertedId.toHexString()
}
Expand Down
4 changes: 2 additions & 2 deletions core/src/utils/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ const encoding = 'hex'

export default class Crypto {
private key: string
private algorithm: string
private algorithm: crypto.CipherGCMTypes

constructor(key: string, algorithm: string = 'aes-256-gcm') {
constructor(key: string, algorithm: crypto.CipherGCMTypes = 'aes-256-gcm') {
this.key = key
this.algorithm = algorithm
}
Expand Down
61 changes: 58 additions & 3 deletions core/test/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,60 @@ const adapters: { [index: string]: IDatabaseAdapter } = {

const randomId = crypto.randomBytes(16).toString('hex')

const USER_UNDEFINED = 'User is not defined';

describe('Database adapter', () => {

describe('Mongo adapter specific', () => {
const adapter = adapters.mongo
const adapterName = 'mongo';

describe('Before init() calls', () => {
it(`${adapterName} insertUser() should fail`, async () => {
expect(
adapter.insertUser({
email: '',
emailConfirmationToken: ''
})
).rejects.toThrow();
});
it(`${adapterName} insertProvider() should fail`, async () => {
expect(
adapter.insertProvider({
userId: '',
login: '',
data: { }
})
).rejects.toThrow();
})
it(`${adapterName} updateUser() should fail`, async () => {
expect(
adapter.updateUser({ id: '', emailConfirmed: true })
).rejects.toThrow();
})
it(`${adapterName} findUserByEmail() should fail`, async () => {
expect(
adapter.findUserByEmail('')
).rejects.toThrow();
})
it(`${adapterName} findUserById() should fail`, async () => {
expect(
adapter.findUserById('')
).rejects.toThrow();
})
it(`${adapterName} findUserByProviderLogin() should fail`, async () => {
expect(
adapter.findUserByProviderLogin('')
).rejects.toThrow();
})
});
});

Object.keys(adapters).forEach(adapterName => {
const adapter = adapters[adapterName]
let userId
let emailConfirmationToken
let userId: string
let emailConfirmationToken: string

it(`${adapterName} init()`, async () => {
await adapter.init()
})
Expand All @@ -61,25 +110,31 @@ describe('Database adapter', () => {
it(`${adapterName} updateUser()`, async () => {
await adapter.updateUser({ id: userId, emailConfirmed: true })
const user = await adapter.findUserById(userId)
if(!user) throw Error(USER_UNDEFINED);
expect(user.emailConfirmed).toEqual(true)
})

it(`${adapterName} findUserByEmail()`, async () => {
const user = await adapter.findUserByEmail(`test+${randomId}@example.com`)
if(!user) throw Error(USER_UNDEFINED);
expect(user.id).toEqual(userId)
expect(user.email).toEqual(`test+${randomId}@example.com`)
emailConfirmationToken = user.emailConfirmationToken
if(user.emailConfirmationToken) {
emailConfirmationToken = user.emailConfirmationToken
}
})

it(`${adapterName} findUserById()`, async () => {
const user = await adapter.findUserById(userId)
if(!user) throw Error(USER_UNDEFINED);
expect(user.id).toEqual(userId)
expect(user.email).toEqual(`test+${randomId}@example.com`)
expect(user.emailConfirmationToken).toEqual(emailConfirmationToken)
})

it(`${adapterName} findUserByProviderLogin()`, async () => {
const user = await adapter.findUserByProviderLogin(`login${randomId}`)
if(!user) throw Error(USER_UNDEFINED);
expect(user.id).toEqual(userId)
expect(user.email).toEqual(`test+${randomId}@example.com`)
expect(user.emailConfirmationToken).toEqual(emailConfirmationToken)
Expand Down
2 changes: 1 addition & 1 deletion core/test/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:
ports:
- '5432:5432'
mysql:
image: mysql
image: mysql:5.7
restart: always
environment:
MYSQL_ROOT_PASSWORD: cleverauth-test
Expand Down
6 changes: 4 additions & 2 deletions core/test/utils/jwt.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ describe('JWT', () => {
expect(original.hello).toEqual(payload.hello)
})
it('sign rejects on invalid params', async () => {
expect(jwt1.sign(undefined)).rejects.toHaveProperty('message', 'payload is required')
const value: unknown = undefined;
expect(jwt1.sign(value as string)).rejects.toHaveProperty('message', 'payload is required')
})
it('verify rejects on invalid params', async () => {
expect(jwt1.verify(undefined)).rejects.toHaveProperty('message', 'jwt must be provided')
const value: unknown = undefined;
expect(jwt1.verify(value as string)).rejects.toHaveProperty('message', 'jwt must be provided')
})
})
Loading

0 comments on commit 7819c96

Please sign in to comment.