Skip to content

Commit

Permalink
Improve self-hosted licence generation (#467)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin committed Feb 22, 2023
1 parent 41f75fb commit 50f4e1a
Show file tree
Hide file tree
Showing 13 changed files with 78 additions and 46 deletions.
1 change: 1 addition & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"start": "nodemon --exec 'yarn fix & ts-node' --esm --files ./src/index.ts",
"create-migration": "scripty",
"create-empty-migration": "scripty",
"hardcode-licence": "scripty",
"migrate": "typeorm-ts-node-esm -d src/db/index.ts migration:run",
"revert": "typeorm-ts-node-esm -d src/db/index.ts migration:revert",
"lint": "eslint 'src/**/*.ts'",
Expand Down
2 changes: 2 additions & 0 deletions backend/scripts/hardcode-licence.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env sh
./node_modules/.bin/ts-node --esm src/hardcoded.ts $1 $2
3 changes: 2 additions & 1 deletion backend/src/admin/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import {
BackendCapabilities,
MergeUsersPayload,
} from '../common/index.js';
import { getIdentityFromRequest, hashPassword } from '../utils.js';
import { getIdentityFromRequest } from '../utils.js';
import { hashPassword } from '../encryption.js';
import { canSendEmails } from '../email/utils.js';
import { mergeUsers } from '../db/actions/merge.js';

Expand Down
2 changes: 1 addition & 1 deletion backend/src/auth/logins/password-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { UserIdentityEntity } from '../../db/entities/index.js';
import { getIdentityByUsername } from '../../db/actions/users.js';
import { comparePassword } from '../../utils.js';
import { comparePassword } from '../../encryption.js';

export default async function loginUser(
username: string,
Expand Down
2 changes: 1 addition & 1 deletion backend/src/auth/register/register-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { RegisterPayload } from '../../common/index.js';
import { v4 } from 'uuid';
import { hashPassword } from '../../utils.js';
import { hashPassword } from '../../encryption.js';
import { UserIdentityEntity } from '../../db/entities/index.js';
import { getIdentityByUsername, registerUser } from '../../db/actions/users.js';
import { canSendEmails } from '../../email/utils.js';
Expand Down
4 changes: 2 additions & 2 deletions backend/src/db/actions/licences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export async function validateLicence(key: string): Promise<boolean> {
});
return found > 0;
} catch (err) {
console.log('Error while retriving the licence: ', err);
console.log('Error while retrieving the licence: ', err);
return false;
}
});
Expand All @@ -63,7 +63,7 @@ export async function fetchLicence(
};
}
} catch (err) {
console.log('Error while retriving the licence: ', err);
console.log('Error while retrieving the licence: ', err);
return null;
}
return null;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/db/actions/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { transaction } from './transaction.js';
import { AccountType, FullUser } from '../../common/index.js';
import { isSelfHostedAndLicenced } from '../../security/is-licenced.js';
import { v4 } from 'uuid';
import { comparePassword, hashPassword } from '../../utils.js';
import { hashPassword, comparePassword } from '../../encryption.js';
import { saveAndReload } from '../repositories/BaseRepository.js';

export async function getUser(userId: string): Promise<UserEntity | null> {
Expand Down
31 changes: 31 additions & 0 deletions backend/src/encryption.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import bcryptjs from 'bcryptjs';
import crypto from 'crypto-js';

const aes = crypto.AES;
const stringify = crypto.enc.Utf8.stringify;
const { compare, genSalt, hash } = bcryptjs;

export async function hashPassword(clearTextPassword: string): Promise<string> {
const salt = await genSalt();
const hashedPassword = await hash(clearTextPassword, salt);
return hashedPassword;
}

export async function comparePassword(
clearTextPassword: string,
hashedPassword: string
): Promise<boolean> {
const match = await compare(clearTextPassword, hashedPassword);
return match;
}

export function encrypt(clear: string, key: string): string {
const encrypted = aes.encrypt(clear, key).toString();
return encrypted;
}

export function decrypt(encrypted: string, key: string): string {
const bytes = aes.decrypt(encrypted, key);
const clear = stringify(bytes);
return clear;
}
30 changes: 26 additions & 4 deletions backend/src/hardcoded.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,33 @@
import { encrypt, hashPassword } from './utils.js';
import { encrypt, hashPassword } from './encryption.js';
import { v4 as uuid } from 'uuid';
import chalk from 'chalk-template';

if (!process.argv[2]) {
console.log(
chalk`No company name provided. {red Please provide the company name as the first argument}.`
);
process.exit(1);
}

const company = process.argv[2].trim();
const key = process.argv[3] ? process.argv[3].trim() : uuid();

buildHardcodedLicence(key, company);

export async function buildHardcodedLicence(
licenceKey: string,
company: string
): Promise<void> {
console.log('Building hardcoded licence for: ', licenceKey);
console.log(
chalk`Building hardcoded licence for company: {yellow ${company}}`
);
console.log(chalk`Licence key to communicate to them: {yellow ${key}}`);
const hash = await hashPassword(licenceKey);
console.log('Hash: ', hash);
console.log('Encrypted company name: ', encrypt(company, licenceKey));
const encryptedOwner = encrypt(company, licenceKey);
const obj = {
hash,
encryptedOwner,
};
console.log('Copy the following object to the hardcodedLicences array:');
console.log(chalk`{red ${JSON.stringify(obj, null, 2)}}`);
}
2 changes: 1 addition & 1 deletion backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ import stripeRouter from './stripe/router.js';
import slackRouter from './slack/router.js';
import game from './game.js';
import {
hashPassword,
getIdentityFromRequest,
getUserViewFromRequest,
getUserQuota,
} from './utils.js';
import { hashPassword } from './encryption.js';
import {
initSentry,
setupSentryErrorHandler,
Expand Down
8 changes: 7 additions & 1 deletion backend/src/security/is-licenced.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { SelfHostedCheckPayload } from '../common/index.js';
import config from '../config.js';
import fetch from 'node-fetch';
import wait, { comparePassword, decrypt } from '../utils.js';
import wait from '../utils.js';
import { LicenceMetadata } from './../types.js';
import { comparePassword, decrypt } from '../encryption.js';

let licenced: LicenceMetadata | null = null;

Expand Down Expand Up @@ -33,6 +34,11 @@ const hardcodedLicences: HardcodedLicence[] = [
encryptedOwner:
'U2FsdGVkX1+xZTCbhmVh4jBPCZfiJ5kipc0Yeo8bm/8CjEoLG8VK/Z1mwTEKxKlR',
},
{
// BAM
hash: '$2a$10$udpIRa0hWeurSsaXtM0iveT4geuQBuGvnNS9UqczkgxOYzHaPqau.',
encryptedOwner: 'U2FsdGVkX1/DQ2JJ57C+LGM7XBLLg9NDxviOwRwj0pI=',
},
];

export function isSelfHostedAndLicenced() {
Expand Down
31 changes: 0 additions & 31 deletions backend/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { Request } from 'express';
import bcryptjs from 'bcryptjs';
import crypto from 'crypto-js';
import { UserView, UserIdentityEntity } from './db/entities/index.js';
import { getUserView, getUser, getIdentity } from './db/actions/users.js';
import { Quota } from './common/index.js';
import { getNumberOfPosts } from './db/actions/posts.js';

const aes = crypto.AES;
const stringify = crypto.enc.Utf8.stringify;
const { compare, genSalt, hash } = bcryptjs;

export async function getUserViewFromRequest(
request: Request
): Promise<UserView | null> {
Expand Down Expand Up @@ -52,31 +46,6 @@ export async function getIdentityFromRequest(
return null;
}

export async function hashPassword(clearTextPassword: string): Promise<string> {
const salt = await genSalt();
const hashedPassword = await hash(clearTextPassword, salt);
return hashedPassword;
}

export async function comparePassword(
clearTextPassword: string,
hashedPassword: string
): Promise<boolean> {
const match = await compare(clearTextPassword, hashedPassword);
return match;
}

export function encrypt(clear: string, key: string): string {
const encrypted = aes.encrypt(clear, key).toString();
return encrypted;
}

export function decrypt(encrypted: string, key: string): string {
const bytes = aes.decrypt(encrypted, key);
const clear = stringify(bytes);
return clear;
}

export default async function wait(delay = 1000) {
return new Promise((resolve) => {
setTimeout(resolve, delay);
Expand Down
6 changes: 3 additions & 3 deletions docs/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4833,9 +4833,9 @@ htmlparser2@^8.0.1:
entities "^4.3.0"

http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==
version "4.1.1"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==

http-deceiver@^1.2.7:
version "1.2.7"
Expand Down

0 comments on commit 50f4e1a

Please sign in to comment.