0
Fork 0
mirror of https://github.com/verdaccio/verdaccio.git synced 2024-12-30 22:34:10 -05:00

refactor: create cryto utils

This commit is contained in:
Juan Picado @jotadeveloper 2018-06-01 22:17:01 +02:00
parent 4a5116fb5d
commit 87e3faa624
No known key found for this signature in database
GPG key ID: 18AC54485952D158
10 changed files with 75 additions and 66 deletions

View file

@ -17,13 +17,13 @@ export default function(route: Router, auth: IAuth) {
route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req: $RequestExtend, res: $Response, next: $NextFunctionVer) { route.put('/-/user/:org_couchdb_user/:_rev?/:revision?', function(req: $RequestExtend, res: $Response, next: $NextFunctionVer) {
let token = (req.body.name && req.body.password) let token = (req.body.name && req.body.password)
? auth.aes_encrypt(new Buffer(req.body.name + ':' + req.body.password)).toString('base64') ? auth.aesEncrypt(new Buffer(req.body.name + ':' + req.body.password)).toString('base64')
: undefined; : undefined;
if (_.isNil(req.remote_user.name) === false) { if (_.isNil(req.remote_user.name) === false) {
res.status(201); res.status(201);
return next({ return next({
ok: 'you are authenticated as \'' + req.remote_user.name + '\'', ok: 'you are authenticated as \'' + req.remote_user.name + '\'',
token: token, token,
}); });
} else { } else {
auth.add_user(req.body.name, req.body.password, function(err, user) { auth.add_user(req.body.name, req.body.password, function(err, user) {

View file

@ -1,6 +1,5 @@
// @flow // @flow
import crypto from 'crypto';
import _ from 'lodash'; import _ from 'lodash';
import { import {
validate_name as utilValidateName, validate_name as utilValidateName,
@ -8,6 +7,7 @@ import {
isObject, isObject,
ErrorCode} from '../lib/utils'; ErrorCode} from '../lib/utils';
import {HEADERS} from '../lib/constants'; import {HEADERS} from '../lib/constants';
import {stringToMD5} from '../lib/crypto-utils';
import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth} from '../../types'; import type {$ResponseExtend, $RequestExtend, $NextFunctionVer, IAuth} from '../../types';
import type {Config} from '@verdaccio/types'; import type {Config} from '@verdaccio/types';
@ -97,18 +97,6 @@ export function anti_loop(config: Config) {
}; };
} }
/**
* Express doesn't do etags with requests <= 1024b
* we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
* could improve performance using crc32 after benchmarks.
* @param {Object} data
* @return {String}
*/
function md5sum(data) {
return crypto.createHash('md5').update(data).digest('hex');
}
export function allow(auth: IAuth) { export function allow(auth: IAuth) {
return function(action: string) { return function(action: string) {
return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) { return function(req: $RequestExtend, res: $ResponseExtend, next: $NextFunctionVer) {
@ -155,7 +143,7 @@ export function allow(auth: IAuth) {
// don't send etags with errors // don't send etags with errors
if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) { if (!res.statusCode || (res.statusCode >= 200 && res.statusCode < 300)) {
res.header('ETag', '"' + md5sum(body) + '"'); res.header('ETag', '"' + stringToMD5(body) + '"');
} }
} else { } else {
// send(null), send(204), etc. // send(null), send(204), etc.

View file

@ -1,13 +1,13 @@
// @flow // @flow
import {loadPlugin} from '../lib/plugin-loader'; import {loadPlugin} from '../lib/plugin-loader';
import Crypto from 'crypto';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import {ErrorCode} from './utils'; import {ErrorCode} from './utils';
import type {Config, Logger, Callback} from '@verdaccio/types'; import type {Config, Logger, Callback} from '@verdaccio/types';
import type {$Response, NextFunction} from 'express'; import type {$Response, NextFunction} from 'express';
import type {$RequestExtend} from '../../types'; import type {$RequestExtend} from '../../types';
import {aesDecrypt, aesEncrypt} from './crypto-utils';
const LoggerApi = require('./logger'); const LoggerApi = require('./logger');
/** /**
@ -254,7 +254,9 @@ class Auth {
this.logger.warn('basic authentication is deprecated, please use JWT instead'); this.logger.warn('basic authentication is deprecated, please use JWT instead');
return credentials; return credentials;
} else if (scheme.toUpperCase() === 'BEARER') { } else if (scheme.toUpperCase() === 'BEARER') {
credentials = this.aes_decrypt(new Buffer(parts[1], 'base64')).toString('utf8'); const token = new Buffer(parts[1], 'base64');
credentials = aesDecrypt(token, this.secret).toString('utf8');
return credentials; return credentials;
} else { } else {
return; return;
@ -331,25 +333,8 @@ class Auth {
/** /**
* Encrypt a string. * Encrypt a string.
*/ */
aes_encrypt(buf: Buffer): Buffer { aesEncrypt(buf: Buffer): Buffer {
const c = Crypto.createCipher('aes192', this.secret); return aesEncrypt(buf, this.secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
/**
* Dencrypt a string.
*/
aes_decrypt(buf: Buffer ) {
try {
const c = Crypto.createDecipher('aes192', this.secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return new Buffer(0);
}
} }
} }

View file

@ -1,7 +1,8 @@
import {generateRandomHexString} from './crypto-utils';
const assert = require('assert'); const assert = require('assert');
const _ = require('lodash'); const _ = require('lodash');
const Error = require('http-errors'); const Error = require('http-errors');
const Crypto = require('crypto');
const minimatch = require('minimatch'); const minimatch = require('minimatch');
const Utils = require('./utils'); const Utils = require('./utils');
@ -164,7 +165,7 @@ class Config {
// unique identifier of self server (or a cluster), used to avoid loops // unique identifier of self server (or a cluster), used to avoid loops
if (!self.server_id) { if (!self.server_id) {
self.server_id = Crypto.pseudoRandomBytes(6).toString('hex'); self.server_id = generateRandomHexString(6);
} }
} }
@ -209,7 +210,7 @@ class Config {
} }
// it generates a secret key // it generates a secret key
// FUTURE: this might be an external secret key, perhaps whitin config file? // FUTURE: this might be an external secret key, perhaps whitin config file?
this.secret = Crypto.pseudoRandomBytes(32).toString('hex'); this.secret = generateRandomHexString(32);
return this.secret; return this.secret;
} }
} }

43
src/lib/crypto-utils.js Normal file
View file

@ -0,0 +1,43 @@
// @flow
import {createDecipher, createCipher, createHash, pseudoRandomBytes} from 'crypto';
export const defaultAlgorithm = 'aes192';
export function aesEncrypt(buf: Buffer, secret: string): Buffer {
const c = createCipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
}
export function aesDecrypt(buf: Buffer, secret: string) {
try {
const c = createDecipher(defaultAlgorithm, secret);
const b1 = c.update(buf);
const b2 = c.final();
return Buffer.concat([b1, b2]);
} catch (_) {
return new Buffer(0);
}
}
export function createTarballHash() {
return createHash('sha1');
}
/**
* Express doesn't do etags with requests <= 1024b
* we use md5 here, it works well on 1k+ bytes, but sucks with fewer data
* could improve performance using crc32 after benchmarks.
* @param {Object} data
* @return {String}
*/
export function stringToMD5(data: Buffer | string) {
return createHash('md5').update(data).digest('hex');
}
export function generateRandomHexString(length: number = 8) {
return pseudoRandomBytes(length).toString('hex');
}

View file

@ -2,7 +2,6 @@
/* eslint prefer-rest-params: 0 */ /* eslint prefer-rest-params: 0 */
import Crypto from 'crypto';
import assert from 'assert'; import assert from 'assert';
import fs from 'fs'; import fs from 'fs';
import Path from 'path'; import Path from 'path';
@ -13,27 +12,28 @@ import async from 'async';
import {ErrorCode, isObject, getLatestVersion, tagVersion, validate_name, semverSort, DIST_TAGS} from './utils'; import {ErrorCode, isObject, getLatestVersion, tagVersion, validate_name, semverSort, DIST_TAGS} from './utils';
import { import {
generatePackageTemplate, normalizePackage, generateRevision, getLatestReadme, cleanUpReadme, generatePackageTemplate, normalizePackage, generateRevision, getLatestReadme, cleanUpReadme,
fileExist, noSuchFile, DEFAULT_REVISION, pkgFileName, fileExist, noSuchFile, DEFAULT_REVISION, pkgFileName,
} from './storage-utils'; } from './storage-utils';
import {createTarballHash} from './crypto-utils';
import {loadPlugin} from '../lib/plugin-loader'; import {loadPlugin} from '../lib/plugin-loader';
import LocalDatabase from '@verdaccio/local-storage'; import LocalDatabase from '@verdaccio/local-storage';
import {UploadTarball, ReadTarball} from '@verdaccio/streams'; import {UploadTarball, ReadTarball} from '@verdaccio/streams';
import type { import type {
Package, Package,
Config, Config,
MergeTags, MergeTags,
Version, Version,
DistFile, DistFile,
Callback, Callback,
Logger, Logger,
} from '@verdaccio/types'; } from '@verdaccio/types';
import type { import type {
ILocalData, ILocalData,
IPackageStorage, IPackageStorage,
} from '@verdaccio/local-storage'; } from '@verdaccio/local-storage';
import type { import type {
IUploadTarball, IUploadTarball,
IReadTarball, IReadTarball,
} from '@verdaccio/streams'; } from '@verdaccio/streams';
import type {IStorage, StringValue} from '../../types'; import type {IStorage, StringValue} from '../../types';
@ -386,7 +386,7 @@ class LocalStorage implements IStorage {
assert(validate_name(filename)); assert(validate_name(filename));
let length = 0; let length = 0;
const shaOneHash = Crypto.createHash('sha1'); const shaOneHash = createTarballHash();
const uploadStream: IUploadTarball = new UploadTarball(); const uploadStream: IUploadTarball = new UploadTarball();
const _transform = uploadStream._transform; const _transform = uploadStream._transform;
const storage = this._getLocalStorage(name); const storage = this._getLocalStorage(name);

View file

@ -1,9 +1,9 @@
// @flow // @flow
import _ from 'lodash'; import _ from 'lodash';
import crypto from 'crypto';
import {ErrorCode, isObject, normalizeDistTags, DIST_TAGS} from './utils'; import {ErrorCode, isObject, normalizeDistTags, DIST_TAGS} from './utils';
import Search from './search'; import Search from './search';
import {generateRandomHexString} from '../lib/crypto-utils';
import type {Package, Version} from '@verdaccio/types'; import type {Package, Version} from '@verdaccio/types';
import type {IStorage} from '../../types'; import type {IStorage} from '../../types';
@ -60,7 +60,7 @@ function normalizePackage(pkg: Package) {
function generateRevision(rev: string): string { function generateRevision(rev: string): string {
const _rev = rev.split('-'); const _rev = rev.split('-');
return ((+_rev[0] || 0) + 1) + '-' + crypto.pseudoRandomBytes(8).toString('hex'); return ((+_rev[0] || 0) + 1) + '-' + generateRandomHexString();
} }
function getLatestReadme(pkg: Package): string { function getLatestReadme(pkg: Package): string {

View file

@ -1,13 +1,5 @@
// @flow // @flow
import crypto from 'crypto';
export function spliceURL(...args: Array<string>): string { export function spliceURL(...args: Array<string>): string {
return Array.from(args).reduce((lastResult, current) => lastResult + current).replace(/([^:])(\/)+(.)/g, `$1/$3`); return Array.from(args).reduce((lastResult, current) => lastResult + current).replace(/([^:])(\/)+(.)/g, `$1/$3`);
} }
/**
* Get MD5 from string
*/
export function stringToMD5(string: string): string {
return crypto.createHash('md5').update(string).digest('hex');
}

View file

@ -1,5 +1,5 @@
// @flow // @flow
import {stringToMD5} from './string'; import {stringToMD5} from '../lib/crypto-utils';
export const GRAVATAR_DEFAULT = 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm'; export const GRAVATAR_DEFAULT = 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm';

View file

@ -23,7 +23,7 @@ export interface IAuth {
logger: Logger; logger: Logger;
secret: string; secret: string;
plugins: Array<any>; plugins: Array<any>;
aes_encrypt(buf: Buffer): Buffer; aesEncrypt(buf: Buffer): Buffer;
apiJWTmiddleware(): $NextFunctionVer; apiJWTmiddleware(): $NextFunctionVer;
webUIJWTmiddleware(): $NextFunctionVer; webUIJWTmiddleware(): $NextFunctionVer;
authenticate(user: string, password: string, cb: Callback): void; authenticate(user: string, password: string, cb: Callback): void;