diff --git a/package.json b/package.json index 42e485fbc..b480472f5 100644 --- a/package.json +++ b/package.json @@ -78,7 +78,7 @@ "@commitlint/config-conventional": "7.1.2", "@material-ui/core": "3.1.0", "@material-ui/icons": "3.0.1", - "@verdaccio/types": "4.0.0", + "@verdaccio/types": "4.1.3", "autosuggest-highlight": "3.1.1", "babel-core": "7.0.0-bridge.0", "babel-eslint": "10.0.1", diff --git a/src/api/web/api.js b/src/api/web/api.js index a79d6cb50..24493da6e 100644 --- a/src/api/web/api.js +++ b/src/api/web/api.js @@ -35,7 +35,7 @@ export default function(config: Config, auth: IAuth, storage: IStorageHandler) { route.use(auth.webUIJWTmiddleware()); route.use(securityIframe); - addPackageWebApi(route, storage, auth); + addPackageWebApi(route, storage, auth, config); addSearchWebApi(route, storage, auth); addUserAuthApi(route, auth, config); diff --git a/src/api/web/endpoint/package.js b/src/api/web/endpoint/package.js index 07745566e..3f3a333f4 100644 --- a/src/api/web/endpoint/package.js +++ b/src/api/web/endpoint/package.js @@ -9,8 +9,9 @@ import { allow } from '../../middleware'; import { DIST_TAGS, HEADER_TYPE, HEADERS, HTTP_STATUS } from '../../../lib/constants'; import type { Router } from 'express'; import type { IAuth, $ResponseExtend, $RequestExtend, $NextFunctionVer, IStorageHandler, $SidebarPackage } from '../../../../types'; +import type { Config } from '@verdaccio/types'; -function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth) { +function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth, config: Config) { const can = allow(auth); const checkAllow = (name, remoteUser) => @@ -86,7 +87,11 @@ function addPackageWebApi(route: Router, storage: IStorageHandler, auth: IAuth) let sideBarInfo: any = _.clone(info); sideBarInfo.latest = info.versions[info[DIST_TAGS].latest]; sideBarInfo = deleteProperties(['readme', '_attachments', '_rev', 'name'], sideBarInfo); - sideBarInfo = addGravatarSupport(sideBarInfo); + if (config.web) { + sideBarInfo = addGravatarSupport(sideBarInfo, config.web.gravatar); + } else { + sideBarInfo = addGravatarSupport(sideBarInfo); + } next(sideBarInfo); } else { res.status(HTTP_STATUS.NOT_FOUND); diff --git a/src/lib/config-utils.js b/src/lib/config-utils.js index e9d13720b..d000a638d 100644 --- a/src/lib/config-utils.js +++ b/src/lib/config-utils.js @@ -111,7 +111,7 @@ export function normalisePackageAccess(packages: PackageList): PackageList { const normalizedPkgs: PackageList = { ...packages }; // add a default rule for all packages to make writing plugins easier if (_.isNil(normalizedPkgs['**'])) { - normalizedPkgs['**'] = { access: [], publish: [] }; + normalizedPkgs['**'] = { access: [], publish: [], proxy: [] }; } for (const pkg in packages) { diff --git a/src/lib/utils.js b/src/lib/utils.js index d342010ca..561a65dd4 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -13,7 +13,7 @@ import createError from 'http-errors'; import marked from 'marked'; import { HTTP_STATUS, API_ERROR, DEFAULT_PORT, DEFAULT_DOMAIN, DEFAULT_PROTOCOL, CHARACTER_ENCODING, HEADERS, DIST_TAGS } from './constants'; -import { generateGravatarUrl, GRAVATAR_DEFAULT } from '../utils/user'; +import { generateGravatarUrl, GENERIC_AVATAR } from '../utils/user'; import type { Package } from '@verdaccio/types'; import type { $Request } from 'express'; @@ -434,7 +434,7 @@ export function deleteProperties(propertiesToDelete: Array, objectItem: return objectItem; } -export function addGravatarSupport(pkgInfo: Object): Object { +export function addGravatarSupport(pkgInfo: Object, online: boolean = true): Object { const pkgInfoCopy = { ...pkgInfo }; const author = _.get(pkgInfo, 'latest.author', null); const contributors = normalizeContributors(_.get(pkgInfo, 'latest.contributors', [])); @@ -442,12 +442,12 @@ export function addGravatarSupport(pkgInfo: Object): Object { // for author. if (author && _.isObject(author)) { - pkgInfoCopy.latest.author.avatar = generateGravatarUrl(author.email); + pkgInfoCopy.latest.author.avatar = generateGravatarUrl(author.email, online); } if (author && _.isString(author)) { pkgInfoCopy.latest.author = { - avatar: generateGravatarUrl(), + avatar: GENERIC_AVATAR, email: '', author, }; @@ -458,10 +458,10 @@ export function addGravatarSupport(pkgInfo: Object): Object { pkgInfoCopy.latest.contributors = contributors.map(contributor => { if (isObject(contributor)) { // $FlowFixMe - contributor.avatar = generateGravatarUrl(contributor.email); + contributor.avatar = generateGravatarUrl(contributor.email, online); } else if (_.isString(contributor)) { contributor = { - avatar: GRAVATAR_DEFAULT, + avatar: GENERIC_AVATAR, email: contributor, name: contributor, }; @@ -474,7 +474,7 @@ export function addGravatarSupport(pkgInfo: Object): Object { // for maintainers if (_.isEmpty(maintainers) === false) { pkgInfoCopy.latest.maintainers = maintainers.map(maintainer => { - maintainer.avatar = generateGravatarUrl(maintainer.email); + maintainer.avatar = generateGravatarUrl(maintainer.email, online); return maintainer; }); } diff --git a/src/utils/user.js b/src/utils/user.js index 1a751ba85..9369a20f0 100644 --- a/src/utils/user.js +++ b/src/utils/user.js @@ -2,17 +2,47 @@ import {stringToMD5} from '../lib/crypto-utils'; import _ from 'lodash'; -export const GRAVATAR_DEFAULT = - 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm'; +// this is a generic avatar +// https://www.iconfinder.com/icons/403017/anonym_avatar_default_head_person_unknown_user_icon +// license: free commercial usage +export const GENERIC_AVATAR: string = ` + data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjwhRE9DVFlQRSBzdmcgIFBVQkxJQyAnLS8vVzNDLy9EVEQgU1 + ZHIDEuMS8vRU4nICAnaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkJz48c3ZnIGVuYWJsZS1iYW + NrZ3JvdW5kPSJuZXcgLTI3IDI0IDEwMCAxMDAiIGhlaWdodD0iMTAwcHgiIGlkPSJ1bmtub3duIiB2ZXJzaW9uPSIxLjEiIHZpZXdCb3 + g9Ii0yNyAyNCAxMDAgMTAwIiB3aWR0aD0iMTAwcHgiIHhtbDpzcGFjZT0icHJlc2VydmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy + 8yMDAwL3N2ZyIgeG1sbnM6c2tldGNoPSJodHRwOi8vd3d3LmJvaGVtaWFuY29kaW5nLmNvbS9za2V0Y2gvbnMiIHhtbG5zOnhsaW5rPS + JodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj48Zz48Zz48ZGVmcz48Y2lyY2xlIGN4PSIyMyIgY3k9Ijc0IiBpZD0iY2lyY2xlIi + ByPSI1MCIvPjwvZGVmcz48dXNlIGZpbGw9IiNGNUVFRTUiIG92ZXJmbG93PSJ2aXNpYmxlIiB4bGluazpocmVmPSIjY2lyY2xlIi8+PGN + saXBQYXRoIGlkPSJjaXJjbGVfMV8iPjx1c2Ugb3ZlcmZsb3c9InZpc2libGUiIHhsaW5rOmhyZWY9IiNjaXJjbGUiLz48L2NsaXBQYXR + oPjxnIGNsaXAtcGF0aD0idXJsKCNjaXJjbGVfMV8pIj48ZGVmcz48cGF0aCBkPSJNMzYsOTUuOWMwLDQsNC43LDUuMiw3LjEsNS44Yzc + uNiwyLDIyLjgsNS45LDIyLjgsNS45YzMuMiwxLjEsNS43LDMuNSw3LjEsNi42djkuOEgtMjd2LTkuOCAgICAgICBjMS4zLTMuMSwzLjk + tNS41LDcuMS02LjZjMCwwLDE1LjItMy45LDIyLjgtNS45YzIuNC0wLjYsNy4xLTEuOCw3LjEtNS44YzAtNCwwLTEwLjksMC0xMC45aDI + 2QzM2LDg1LDM2LDkxLjksMzYsOTUuOXoiIGlkPSJzaG91bGRlcnMiLz48L2RlZnM+PHVzZSBmaWxsPSIjRTZDMTlDIiBvdmVyZmxvdz0 + idmlzaWJsZSIgeGxpbms6aHJlZj0iI3Nob3VsZGVycyIvPjxjbGlwUGF0aCBpZD0ic2hvdWxkZXJzXzFfIj48dXNlIG92ZXJmbG93PSJ + 2aXNpYmxlIiB4bGluazpocmVmPSIjc2hvdWxkZXJzIi8+PC9jbGlwUGF0aD48cGF0aCBjbGlwLXBhdGg9InVybCgjc2hvdWxkZXJzXzF + fKSIgZD0iTTIzLjIsMzVjMC4xLDAsMC4xLDAsMC4yLDBjMCwwLDAsMCwwLDAgICAgICBjMy4zLDAsOC4yLDAuMiwxMS40LDJjMy4zLD + EuOSw3LjMsNS42LDguNSwxMi4xYzIuNCwxMy43LTIuMSwzNS40LTYuMyw0Mi40Yy00LDYuNy05LjgsOS4yLTEzLjUsOS40YzAsMC0wL + jEsMC0wLjEsMCAgICAgIGMtMC4xLDAtMC4xLDAtMC4yLDBjLTAuMSwwLTAuMSwwLTAuMiwwYzAsMC0wLjEsMC0wLjEsMGMtMy43LTAuM + i05LjUtMi43LTEzLjUtOS40Yy00LjItNy04LjctMjguNy02LjMtNDIuNCAgICAgIGMxLjItNi41LDUuMi0xMC4yLDguNS0xMi4xYzMuM + i0xLjgsOC4xLTIsMTEuNC0yYzAsMCwwLDAsMCwwQzIzLjEsMzUsMjMuMSwzNSwyMy4yLDM1TDIzLjIsMzV6IiBmaWxsPSIjRDRCMDhDI + iBpZD0iaGVhZC1zaGFkb3ciLz48L2c+PC9nPjxwYXRoIGQ9Ik0yMi42LDQwYzE5LjEsMCwyMC43LDEzLjgsMjAuOCwxNS4xYzEuMSwxM + S45LTMsMjguMS02LjgsMzMuN2MtNCw1LjktOS44LDguMS0xMy41LDguMyAgICBjLTAuMiwwLTAuMiwwLTAuMywwYy0wLjEsMC0wLjEs + MC0wLjIsMEMxOC44LDk2LjgsMTMsOTQuNiw5LDg4LjdjLTMuOC01LjYtNy45LTIxLjgtNi44LTMzLjhDMi4zLDUzLjcsMy41LDQwLDIyL + jYsNDB6IiBmaWxsPSIjRjJDRUE1IiBpZD0iaGVhZCIvPjwvZz48L3N2Zz4=`; + /** * Generate gravatar url from email address */ -export function generateGravatarUrl(email: string = ''): string { +export function generateGravatarUrl(email: string = '', online: boolean = true): string { let emailCopy = email; - if (_.isString(email) && _.size(email) > 0) { - emailCopy = email.trim().toLocaleLowerCase(); - const emailMD5 = stringToMD5(emailCopy); - return `https://www.gravatar.com/avatar/${emailMD5}`; + if (online) { + if (_.isString(email) && _.size(email) > 0) { + emailCopy = email.trim().toLocaleLowerCase(); + const emailMD5 = stringToMD5(emailCopy); + return `https://www.gravatar.com/avatar/${emailMD5}`; + } + return GENERIC_AVATAR; + } else { + return GENERIC_AVATAR; } - return GRAVATAR_DEFAULT; } diff --git a/test/flow/plugins/auth/example.auth.plugin.js b/test/flow/plugins/auth/example.auth.plugin.js index 3136f95b9..25fe3466b 100644 --- a/test/flow/plugins/auth/example.auth.plugin.js +++ b/test/flow/plugins/auth/example.auth.plugin.js @@ -94,8 +94,8 @@ const remoteUser: RemoteUser = { }; auth.authenticate('user', 'pass', () => {}); -auth.allow_access(remoteUser, {}, () => {}); -auth.allow_publish(remoteUser, {}, () => {}); +auth.allow_access(remoteUser, { access: [], publish: [], proxy: [] }, () => {}); +auth.allow_publish(remoteUser, { access: [], publish: [], proxy: [] }, () => {}); authSub.authenticate('user', 'pass', () => {}); -authSub.allow_access(remoteUser, { sub: true }, () => {}); -authSub.allow_publish(remoteUser, { sub: true }, () => {}); +authSub.allow_access(remoteUser, { access: [], publish: [], proxy: [], sub: true }, () => {}); +authSub.allow_publish(remoteUser, { access: [], publish: [], proxy: [], sub: true }, () => {}); diff --git a/test/unit/api/utils.spec.js b/test/unit/api/utils.spec.js index 1fae9e9ca..4e81f1b3d 100644 --- a/test/unit/api/utils.spec.js +++ b/test/unit/api/utils.spec.js @@ -1,5 +1,5 @@ // @flow -import { generateGravatarUrl, GRAVATAR_DEFAULT } from '../../../src/utils/user'; +import {generateGravatarUrl, GENERIC_AVATAR } from '../../../src/utils/user'; import { spliceURL } from '../../../src/utils/string'; import { validateName, @@ -286,7 +286,7 @@ describe('Utilities', () => { test('should generate generic gravatar url', () => { const gravatarUrl: string = generateGravatarUrl(); - expect(gravatarUrl).toMatch(GRAVATAR_DEFAULT); + expect(gravatarUrl).toMatch(GENERIC_AVATAR); }); }); @@ -355,8 +355,7 @@ describe('Utilities', () => { latest: { author: { author: 'user@verdccio.org', - avatar: - 'https://www.gravatar.com/avatar/00000000000000000000000000000000?d=mm', + avatar: GENERIC_AVATAR, email: '' } } @@ -456,7 +455,7 @@ describe('Utilities', () => { latest: { contributors: [ { - avatar: GRAVATAR_DEFAULT, + avatar: GENERIC_AVATAR, email: contributor, name: contributor } diff --git a/yarn.lock b/yarn.lock index c03c6c20b..f86309fb7 100644 Binary files a/yarn.lock and b/yarn.lock differ