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:
parent
4a5116fb5d
commit
87e3faa624
10 changed files with 75 additions and 66 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
43
src/lib/crypto-utils.js
Normal 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');
|
||||||
|
}
|
|
@ -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);
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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');
|
|
||||||
}
|
|
||||||
|
|
|
@ -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';
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue