mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-17 01:17:29 +02:00
fix: User should require a ID field set (#799)
This commit is contained in:
parent
424d4e209e
commit
b0e6d8c363
@ -2,7 +2,7 @@
|
||||
|
||||
import { Knex } from 'knex';
|
||||
import { Logger, LogProvider } from '../logger';
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
|
||||
const NotFoundError = require('../error/notfound-error');
|
||||
|
||||
@ -14,7 +14,6 @@ const USER_COLUMNS = [
|
||||
'username',
|
||||
'email',
|
||||
'image_url',
|
||||
'permissions', // TODO: remove in v4
|
||||
'login_attempts',
|
||||
'seen_at',
|
||||
'created_at',
|
||||
@ -29,14 +28,20 @@ const emptify = value => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const mapUserToColumns = user => ({
|
||||
const mapUserToColumns = (user: ICreateUser) => ({
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
email: user.email,
|
||||
image_url: user.imageUrl,
|
||||
permissions: user.permissions ? JSON.stringify(user.permissions) : null,
|
||||
});
|
||||
|
||||
interface ICreateUser {
|
||||
name?: string;
|
||||
username?: string;
|
||||
email?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
const rowToUser = row => {
|
||||
if (!row) {
|
||||
throw new NotFoundError('No user found');
|
||||
@ -48,7 +53,6 @@ const rowToUser = row => {
|
||||
email: emptify(row.email),
|
||||
imageUrl: emptify(row.image_url),
|
||||
loginAttempts: row.login_attempts,
|
||||
permissions: row.permissions,
|
||||
seenAt: row.seen_at,
|
||||
createdAt: row.created_at,
|
||||
});
|
||||
@ -88,14 +92,14 @@ class UserStore {
|
||||
return this.get({ id });
|
||||
}
|
||||
|
||||
async insert(user: User): Promise<User> {
|
||||
async insert(user: ICreateUser): Promise<User> {
|
||||
const rows = await this.db(TABLE)
|
||||
.insert(mapUserToColumns(user))
|
||||
.returning(USER_COLUMNS);
|
||||
return rowToUser(rows[0]);
|
||||
}
|
||||
|
||||
async upsert(user: User): Promise<User> {
|
||||
async upsert(user: ICreateUser): Promise<User> {
|
||||
const id = await this.hasUser(user);
|
||||
|
||||
if (id) {
|
||||
|
@ -4,9 +4,9 @@ import sinon from 'sinon';
|
||||
|
||||
import apiTokenMiddleware from './api-token-middleware';
|
||||
import getLogger from '../../test/fixtures/no-logger';
|
||||
import User from '../user';
|
||||
import { CLIENT } from '../permissions';
|
||||
import { createTestConfig } from '../../test/config/test-config';
|
||||
import ApiUser from '../types/api-user';
|
||||
|
||||
let config: any;
|
||||
|
||||
@ -60,8 +60,7 @@ test('should not add user if unknown token', async t => {
|
||||
});
|
||||
|
||||
test('should add user if unknown token', async t => {
|
||||
const apiUser = new User({
|
||||
isAPI: true,
|
||||
const apiUser = new ApiUser({
|
||||
username: 'default',
|
||||
permissions: [CLIENT],
|
||||
});
|
||||
@ -86,8 +85,7 @@ test('should add user if unknown token', async t => {
|
||||
});
|
||||
|
||||
test('should not add user if disabled', async t => {
|
||||
const apiUser = new User({
|
||||
isAPI: true,
|
||||
const apiUser = new ApiUser({
|
||||
username: 'default',
|
||||
permissions: [CLIENT],
|
||||
});
|
||||
|
@ -16,15 +16,15 @@ const apiAccessMiddleware = (
|
||||
}
|
||||
|
||||
return (req, res, next) => {
|
||||
if (req.user) {
|
||||
if (req.apiUser) {
|
||||
return next();
|
||||
}
|
||||
|
||||
try {
|
||||
const userToken = req.header('authorization');
|
||||
const user = apiTokenService.getUserForToken(userToken);
|
||||
if (user) {
|
||||
req.user = user;
|
||||
const apiToken = req.header('authorization');
|
||||
const apiUser = apiTokenService.getUserForToken(apiToken);
|
||||
if (apiUser) {
|
||||
req.user = apiUser;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(error);
|
||||
|
@ -1,5 +1,3 @@
|
||||
const auth = require('basic-auth');
|
||||
const User = require('../user');
|
||||
const AuthenticationRequired = require('../authentication-required');
|
||||
|
||||
function demoAuthentication(app, basePath = '', { userService }) {
|
||||
@ -15,11 +13,6 @@ function demoAuthentication(app, basePath = '', { userService }) {
|
||||
app.use(`${basePath}/api/admin/`, (req, res, next) => {
|
||||
if (req.session.user && req.session.user.email) {
|
||||
req.user = req.session.user;
|
||||
} else if (req.header('authorization')) {
|
||||
const user = auth(req);
|
||||
if (user && user.name) {
|
||||
req.user = new User({ username: user.name });
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
@ -1,14 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const { ADMIN } = require('../permissions');
|
||||
const User = require('../user');
|
||||
const ApiUser = require('../types/api-user');
|
||||
|
||||
function noneAuthentication(basePath = '', app) {
|
||||
app.use(`${basePath}/api/admin/`, (req, res, next) => {
|
||||
if (!req.user) {
|
||||
req.user = new User({
|
||||
req.user = new ApiUser({
|
||||
username: 'unknown',
|
||||
isAPI: true,
|
||||
permissions: [ADMIN],
|
||||
});
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ import sinon from 'sinon';
|
||||
|
||||
import rbacMiddleware from './rbac-middleware';
|
||||
import ffStore from '../../test/fixtures/fake-feature-toggle-store';
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
import perms from '../permissions';
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import { createTestConfig } from '../../test/config/test-config';
|
||||
import ApiUser from '../types/api-user';
|
||||
|
||||
let config: IUnleashConfig;
|
||||
let featureToggleStore: any;
|
||||
@ -43,10 +44,9 @@ test('should give api-user ADMIN permission', async t => {
|
||||
|
||||
const cb = sinon.fake();
|
||||
const req: any = {
|
||||
user: new User({
|
||||
user: new ApiUser({
|
||||
username: 'api',
|
||||
permissions: [perms.ADMIN],
|
||||
isAPI: true,
|
||||
}),
|
||||
};
|
||||
|
||||
@ -66,10 +66,9 @@ test('should not give api-user ADMIN permission', async t => {
|
||||
|
||||
const cb = sinon.fake();
|
||||
const req: any = {
|
||||
user: new User({
|
||||
user: new ApiUser({
|
||||
username: 'api',
|
||||
permissions: [perms.CLIENT],
|
||||
isAPI: true,
|
||||
}),
|
||||
};
|
||||
|
||||
@ -90,10 +89,9 @@ test('should not allow user to miss userId', async t => {
|
||||
|
||||
const cb = sinon.fake();
|
||||
const req: any = {
|
||||
user: new User({
|
||||
user: {
|
||||
username: 'user',
|
||||
permissions: [perms.ADMIN],
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
func(req, undefined, cb);
|
||||
|
@ -5,9 +5,10 @@ import {
|
||||
DELETE_FEATURE,
|
||||
ADMIN,
|
||||
} from '../permissions';
|
||||
import ApiUser from '../types/api-user';
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
|
||||
interface PermissionChecker {
|
||||
hasPermission(
|
||||
@ -34,7 +35,6 @@ const rbacMiddleware = (
|
||||
return false;
|
||||
}
|
||||
|
||||
// Support ADMIN API tokens for enterpriseAuthentication.
|
||||
if (user.isAPI) {
|
||||
return user.permissions.includes(ADMIN);
|
||||
}
|
||||
|
@ -12,8 +12,9 @@ import { Logger } from '../../logger';
|
||||
import { ApiTokenType } from '../../db/api-token-store';
|
||||
import { AccessService } from '../../services/access-service';
|
||||
import { IAuthRequest } from '../unleash-types';
|
||||
import User from '../../user';
|
||||
import User from '../../types/user';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import ApiUser from '../../types/api-user';
|
||||
|
||||
interface IServices {
|
||||
apiTokenService: ApiTokenService;
|
||||
@ -39,8 +40,8 @@ class ApiTokenController extends Controller {
|
||||
this.delete('/:token', this.deleteApiToken, DELETE_API_TOKEN);
|
||||
}
|
||||
|
||||
private async isTokenAdmin(user: User) {
|
||||
if (user.isAPI) {
|
||||
private async isTokenAdmin(user: User | ApiUser) {
|
||||
if (user instanceof ApiUser) {
|
||||
return user.permissions.includes(ADMIN);
|
||||
}
|
||||
|
||||
|
@ -8,11 +8,11 @@ const supertest = require('supertest');
|
||||
const { EventEmitter } = require('events');
|
||||
const store = require('../../../test/fixtures/store');
|
||||
const getApp = require('../../app');
|
||||
const User = require('../../user');
|
||||
const User = require('../../types/user');
|
||||
|
||||
const eventBus = new EventEmitter();
|
||||
|
||||
const currentUser = new User({ email: 'test@mail.com' });
|
||||
const currentUser = new User({ id: 1337, email: 'test@mail.com' });
|
||||
|
||||
function getSetup() {
|
||||
const base = `/random${Math.round(Math.random() * 1000)}`;
|
||||
|
@ -7,7 +7,7 @@ import { AccessService } from '../../services/access-service';
|
||||
import { IUnleashConfig } from '../../types/option';
|
||||
import { IUnleashServices } from '../../types/services';
|
||||
import UserService from '../../services/user-service';
|
||||
import User from '../../user';
|
||||
import User from '../../types/user';
|
||||
import { Logger } from '../../logger';
|
||||
import { handleErrors } from './util';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
const test = require('ava');
|
||||
const request = require('supertest');
|
||||
const express = require('express');
|
||||
const User = require('../../user');
|
||||
const User = require('../../types/user');
|
||||
const PasswordProvider = require('./simple-password-provider');
|
||||
|
||||
const getLogger = () => ({ info: () => {}, error: () => {} });
|
||||
@ -24,7 +24,7 @@ test('Should require password', async t => {
|
||||
test('Should login user', async t => {
|
||||
const username = 'ola';
|
||||
const password = 'simplepass';
|
||||
const user = new User({ username, permissions: ['ADMIN'] });
|
||||
const user = new User({ id: 123, username });
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
@ -55,7 +55,7 @@ test('Should login user', async t => {
|
||||
test('Should not login user with wrong password', async t => {
|
||||
const username = 'ola';
|
||||
const password = 'simplepass';
|
||||
const user = new User({ username, permissions: ['ADMIN'] });
|
||||
const user = new User({ id: 133, username });
|
||||
|
||||
const app = express();
|
||||
app.use(express.json());
|
||||
|
@ -8,11 +8,11 @@ const supertest = require('supertest');
|
||||
const { EventEmitter } = require('events');
|
||||
const store = require('../../test/fixtures/store');
|
||||
const getApp = require('../app');
|
||||
const User = require('../user');
|
||||
const User = require('../types/user');
|
||||
|
||||
const eventBus = new EventEmitter();
|
||||
|
||||
const currentUser = new User({ email: 'test@mail.com' });
|
||||
const currentUser = new User({ id: 1337, email: 'test@mail.com' });
|
||||
|
||||
function getSetup() {
|
||||
const base = `/random${Math.round(Math.random() * 1000)}`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Request } from 'express';
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
|
||||
export interface IAuthRequest extends Request {
|
||||
user: User;
|
||||
|
@ -10,7 +10,7 @@ import { createMetricsMonitor } from './metrics';
|
||||
import { createStores } from './db';
|
||||
import { createServices } from './services';
|
||||
import { createConfig } from './create-config';
|
||||
import User from './user';
|
||||
import User from './types/user';
|
||||
|
||||
import permissions from './permissions';
|
||||
import AuthenticationRequired from './authentication-required';
|
||||
|
@ -5,7 +5,7 @@ import {
|
||||
IUserRole,
|
||||
} from '../db/access-store';
|
||||
import permissions from '../permissions';
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
|
||||
export const ALL_PROJECTS = '*';
|
||||
|
||||
@ -65,6 +65,7 @@ enum PermissionType {
|
||||
}
|
||||
|
||||
export enum RoleName {
|
||||
// eslint-disable-next-line @typescript-eslint/no-shadow
|
||||
ADMIN = 'Admin',
|
||||
EDITOR = 'Editor',
|
||||
VIEWER = 'Viewer',
|
||||
|
@ -1,23 +1,13 @@
|
||||
import crypto from 'crypto';
|
||||
import { ApiTokenStore, IApiToken, ApiTokenType } from '../db/api-token-store';
|
||||
import { Logger, LogProvider } from '../logger';
|
||||
import { Logger } from '../logger';
|
||||
import { ADMIN, CLIENT } from '../permissions';
|
||||
import User from '../user';
|
||||
import { IUnleashStores } from '../types/stores';
|
||||
import { IUnleashConfig } from '../types/option';
|
||||
import ApiUser from '../types/api-user';
|
||||
|
||||
const ONE_MINUTE = 60_000;
|
||||
|
||||
interface IStores {
|
||||
apiTokenStore: ApiTokenStore;
|
||||
settingStore: any;
|
||||
}
|
||||
|
||||
interface IConfig {
|
||||
getLogger: LogProvider;
|
||||
baseUriPath: string;
|
||||
}
|
||||
|
||||
interface CreateTokenRequest {
|
||||
username: string;
|
||||
type: ApiTokenType;
|
||||
@ -63,14 +53,13 @@ export class ApiTokenService {
|
||||
return this.store.getAllActive();
|
||||
}
|
||||
|
||||
public getUserForToken(secret: string): User | undefined {
|
||||
public getUserForToken(secret: string): ApiUser | undefined {
|
||||
const token = this.activeTokens.find(t => t.secret === secret);
|
||||
if (token) {
|
||||
const permissions =
|
||||
token.type === ApiTokenType.ADMIN ? [ADMIN] : [CLIENT];
|
||||
|
||||
return new User({
|
||||
isAPI: true,
|
||||
return new ApiUser({
|
||||
username: token.username,
|
||||
permissions,
|
||||
});
|
||||
@ -101,7 +90,7 @@ export class ApiTokenService {
|
||||
return crypto.randomBytes(32).toString('hex');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
destroy(): void {
|
||||
clearInterval(this.timer);
|
||||
this.timer = null;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import User from '../user';
|
||||
import User from '../types/user';
|
||||
import { AccessService, IUserWithRole, RoleName } from './access-service';
|
||||
import ProjectStore, { IProject } from '../db/project-store';
|
||||
import EventStore from '../db/event-store';
|
||||
|
@ -6,7 +6,7 @@ import Joi from 'joi';
|
||||
import { URL } from 'url';
|
||||
import UserStore, { IUserSearch } from '../db/user-store';
|
||||
import { Logger } from '../logger';
|
||||
import User, { IUser } from '../user';
|
||||
import User, { IUser } from '../types/user';
|
||||
import isEmail from '../util/is-email';
|
||||
import { AccessService, RoleName } from './access-service';
|
||||
import { ADMIN } from '../permissions';
|
||||
@ -110,12 +110,9 @@ class UserService {
|
||||
this.logger.info(
|
||||
'Creating default user "admin" with password "admin"',
|
||||
);
|
||||
const user = await this.store.insert(
|
||||
new User({
|
||||
const user = await this.store.insert({
|
||||
username: 'admin',
|
||||
permissions: [ADMIN], // TODO: remove in v4
|
||||
}),
|
||||
);
|
||||
});
|
||||
const passwordHash = await bcrypt.hash('admin', saltRounds);
|
||||
await this.store.setPasswordHash(user.id, passwordHash);
|
||||
|
||||
@ -180,10 +177,11 @@ class UserService {
|
||||
throw new Error('User already exists');
|
||||
}
|
||||
|
||||
const user = await this.store.insert(
|
||||
// TODO: remove permission in v4.
|
||||
new User({ username, email, name, permissions: [ADMIN] }),
|
||||
);
|
||||
const user = await this.store.insert({
|
||||
username,
|
||||
email,
|
||||
name,
|
||||
});
|
||||
|
||||
await this.accessService.setUserRootRole(user.id, rootRole);
|
||||
|
||||
|
24
src/lib/types/api-user.ts
Normal file
24
src/lib/types/api-user.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { CLIENT } from '../permissions';
|
||||
|
||||
interface IApiUserData {
|
||||
username: string;
|
||||
permissions?: string[];
|
||||
}
|
||||
|
||||
export default class ApiUser {
|
||||
isAPI: boolean = true;
|
||||
|
||||
username: string;
|
||||
|
||||
permissions: string[];
|
||||
|
||||
constructor({ username, permissions = [CLIENT] }: IApiUserData) {
|
||||
if (!username) {
|
||||
throw new TypeError('username is required');
|
||||
}
|
||||
this.username = username;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ApiUser;
|
@ -2,7 +2,7 @@ import { Request } from 'express';
|
||||
import EventEmitter from 'events';
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import User from '../user';
|
||||
import User from './user';
|
||||
import { IUnleashConfig } from './option';
|
||||
import { IUnleashStores } from './stores';
|
||||
import { IUnleashServices } from './services';
|
||||
|
@ -1,10 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('ava');
|
||||
const User = require('./user');
|
||||
import test from 'ava';
|
||||
import User from './user';
|
||||
|
||||
test('should create user', t => {
|
||||
const user = new User({ name: 'ole', email: 'some@email.com' });
|
||||
const user = new User({ id: 11, name: 'ole', email: 'some@email.com' });
|
||||
t.is(user.name, 'ole');
|
||||
t.is(user.email, 'some@email.com');
|
||||
t.is(
|
||||
@ -15,15 +13,14 @@ test('should create user', t => {
|
||||
|
||||
test('should create user, all fields', t => {
|
||||
const user = new User({
|
||||
id: 11,
|
||||
name: 'Admin',
|
||||
username: 'admin',
|
||||
email: 'some@email.com',
|
||||
permissions: ['admin', 'client'],
|
||||
});
|
||||
t.is(user.name, 'Admin');
|
||||
t.is(user.username, 'admin');
|
||||
t.is(user.email, 'some@email.com');
|
||||
t.deepEqual(user.permissions, ['admin', 'client']);
|
||||
t.is(
|
||||
user.imageUrl,
|
||||
'https://gravatar.com/avatar/d8ffeba65ee5baf57e4901690edc8e1b?size=42&default=retro',
|
||||
@ -33,7 +30,7 @@ test('should create user, all fields', t => {
|
||||
test('should require email or username', t => {
|
||||
const error = t.throws(
|
||||
() => {
|
||||
const user = new User(); // eslint-disable-line
|
||||
const user = new User({ id: 11 }); // eslint-disable-line
|
||||
},
|
||||
{ instanceOf: Error },
|
||||
);
|
||||
@ -42,7 +39,7 @@ test('should require email or username', t => {
|
||||
});
|
||||
|
||||
test('Should create user with only email defined', t => {
|
||||
const user = new User({ email: 'some@email.com' });
|
||||
const user = new User({ id: 123, email: 'some@email.com' });
|
||||
|
||||
t.is(user.email, 'some@email.com');
|
||||
});
|
||||
@ -50,7 +47,7 @@ test('Should create user with only email defined', t => {
|
||||
test('Should require valid email', t => {
|
||||
const error = t.throws(
|
||||
() => {
|
||||
new User({ email: 'some@' }); // eslint-disable-line
|
||||
new User({ id: 11, email: 'some@' }); // eslint-disable-line
|
||||
},
|
||||
{ instanceOf: Error },
|
||||
);
|
||||
@ -59,7 +56,7 @@ test('Should require valid email', t => {
|
||||
});
|
||||
|
||||
test('Should create user with only username defined', t => {
|
||||
const user = new User({ username: 'some-user' });
|
||||
const user = new User({ id: 133, username: 'some-user' });
|
||||
t.is(user.username, 'some-user');
|
||||
t.is(
|
||||
user.imageUrl,
|
||||
@ -68,6 +65,10 @@ test('Should create user with only username defined', t => {
|
||||
});
|
||||
|
||||
test('Should create user with only username defined and undefined email', t => {
|
||||
const user = new User({ username: 'some-user', email: undefined });
|
||||
const user = new User({
|
||||
id: 1447,
|
||||
username: 'some-user',
|
||||
email: undefined,
|
||||
});
|
||||
t.is(user.username, 'some-user');
|
||||
});
|
@ -2,12 +2,10 @@ import gravatarUrl from 'gravatar-url';
|
||||
import Joi from 'joi';
|
||||
|
||||
export interface UserData {
|
||||
id?: number;
|
||||
isAPI?: boolean;
|
||||
id: number;
|
||||
name?: string;
|
||||
username?: string;
|
||||
email?: string;
|
||||
permissions?: string[];
|
||||
imageUrl?: string;
|
||||
seenAt?: Date;
|
||||
loginAttempts?: number;
|
||||
@ -25,8 +23,6 @@ export interface IUser {
|
||||
export default class User implements IUser {
|
||||
id: number;
|
||||
|
||||
isAPI: boolean;
|
||||
|
||||
name: string;
|
||||
|
||||
username: string;
|
||||
@ -43,20 +39,19 @@ export default class User implements IUser {
|
||||
|
||||
createdAt: Date;
|
||||
|
||||
constructor(
|
||||
{
|
||||
constructor({
|
||||
id,
|
||||
isAPI,
|
||||
name,
|
||||
email,
|
||||
username,
|
||||
imageUrl,
|
||||
permissions,
|
||||
seenAt,
|
||||
loginAttempts,
|
||||
createdAt,
|
||||
}: UserData = { isAPI: false },
|
||||
) {
|
||||
}: UserData) {
|
||||
if (!id) {
|
||||
throw new TypeError('Id is required');
|
||||
}
|
||||
if (!username && !email) {
|
||||
throw new TypeError('Username or Email is required');
|
||||
}
|
||||
@ -65,11 +60,9 @@ export default class User implements IUser {
|
||||
Joi.assert(name, Joi.string(), 'Name');
|
||||
|
||||
this.id = id;
|
||||
this.isAPI = isAPI;
|
||||
this.name = name;
|
||||
this.username = username;
|
||||
this.email = email;
|
||||
this.permissions = permissions;
|
||||
this.imageUrl = imageUrl || this.generateImageUrl();
|
||||
this.seenAt = seenAt;
|
||||
this.loginAttempts = loginAttempts;
|
@ -2,7 +2,7 @@ import test from 'ava';
|
||||
import { setupApp } from '../../helpers/test-helper';
|
||||
import dbInit from '../../helpers/database-init';
|
||||
import getLogger from '../../../fixtures/no-logger';
|
||||
import User from '../../../../lib/user';
|
||||
import User from '../../../../lib/types/user';
|
||||
import UserStore from '../../../../lib/db/user-store';
|
||||
import { AccessStore, IRole } from '../../../../lib/db/access-store';
|
||||
import { RoleName } from '../../../../lib/services/access-service';
|
||||
@ -164,7 +164,7 @@ test.serial('update user name', async t => {
|
||||
test.serial('should delete user', async t => {
|
||||
t.plan(0);
|
||||
|
||||
const user = await userStore.insert(new User({ email: 'some@mail.com' }));
|
||||
const user = await userStore.insert({ email: 'some@mail.com' });
|
||||
|
||||
const request = await setupApp(stores);
|
||||
return request.delete(`/api/admin/user-admin/${user.id}`).expect(200);
|
||||
@ -193,7 +193,7 @@ test.serial('validator should accept strong password', async t => {
|
||||
test.serial('should change password', async t => {
|
||||
t.plan(0);
|
||||
|
||||
const user = await userStore.insert(new User({ email: 'some@mail.com' }));
|
||||
const user = await userStore.insert({ email: 'some@mail.com' });
|
||||
|
||||
const request = await setupApp(stores);
|
||||
return request
|
||||
@ -205,9 +205,9 @@ test.serial('should change password', async t => {
|
||||
test.serial('should search for users', async t => {
|
||||
t.plan(2);
|
||||
|
||||
await userStore.insert(new User({ email: 'some@mail.com' }));
|
||||
await userStore.insert(new User({ email: 'another@mail.com' }));
|
||||
await userStore.insert(new User({ email: 'another2@mail.com' }));
|
||||
await userStore.insert({ email: 'some@mail.com' });
|
||||
await userStore.insert({ email: 'another@mail.com' });
|
||||
await userStore.insert({ email: 'another2@mail.com' });
|
||||
|
||||
const request = await setupApp(stores);
|
||||
return request
|
||||
|
@ -11,7 +11,7 @@ import ResetTokenService from '../../../../lib/services/reset-token-service';
|
||||
import UserService from '../../../../lib/services/user-service';
|
||||
import { setupApp } from '../../helpers/test-helper';
|
||||
import { EmailService } from '../../../../lib/services/email-service';
|
||||
import User from '../../../../lib/user';
|
||||
import User from '../../../../lib/types/user';
|
||||
import { IUnleashConfig } from '../../../../lib/types/option';
|
||||
import { createTestConfig } from '../../../config/test-config';
|
||||
|
||||
|
@ -9,7 +9,6 @@ const {
|
||||
ALL_PROJECTS,
|
||||
} = require('../../../lib/services/access-service');
|
||||
const permissions = require('../../../lib/permissions');
|
||||
const User = require('../../../lib/user');
|
||||
|
||||
let db;
|
||||
let stores;
|
||||
@ -23,16 +22,17 @@ let readRole;
|
||||
|
||||
const createUserEditorAccess = async (name, email) => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(new User({ name, email }));
|
||||
const user = await userStore.insert({ name, email });
|
||||
await accessService.addUserToRole(user.id, editorRole.id);
|
||||
return user;
|
||||
};
|
||||
|
||||
const createSuperUser = async () => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Alice Admin', email: 'admin@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Alice Admin',
|
||||
email: 'admin@getunleash.io',
|
||||
});
|
||||
await accessService.addUserToRole(user.id, adminRole.id);
|
||||
return user;
|
||||
};
|
||||
@ -293,9 +293,10 @@ test.serial('should not get access if not specifying project', async t => {
|
||||
|
||||
test.serial('should remove user from role', async t => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Some User', email: 'random123@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
email: 'random123@getunleash.io',
|
||||
});
|
||||
|
||||
await accessService.addUserToRole(user.id, editorRole.id);
|
||||
|
||||
@ -311,9 +312,10 @@ test.serial('should remove user from role', async t => {
|
||||
|
||||
test.serial('should return role with users', async t => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Some User', email: 'random2223@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
email: 'random2223@getunleash.io',
|
||||
});
|
||||
|
||||
await accessService.addUserToRole(user.id, editorRole.id);
|
||||
|
||||
@ -327,9 +329,10 @@ test.serial('should return role with users', async t => {
|
||||
|
||||
test.serial('should return role with permissions and users', async t => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Some User', email: 'random2244@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
email: 'random2244@getunleash.io',
|
||||
});
|
||||
|
||||
await accessService.addUserToRole(user.id, editorRole.id);
|
||||
|
||||
@ -368,9 +371,10 @@ test.serial('should return list of permissions', async t => {
|
||||
|
||||
test.serial('should set root role for user', async t => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Some User', email: 'random2255@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
email: 'random2255@getunleash.io',
|
||||
});
|
||||
|
||||
await accessService.setUserRootRole(user.id, editorRole.id);
|
||||
|
||||
@ -382,9 +386,10 @@ test.serial('should set root role for user', async t => {
|
||||
|
||||
test.serial('should switch root role for user', async t => {
|
||||
const { userStore } = stores;
|
||||
const user = await userStore.insert(
|
||||
new User({ name: 'Some User', email: 'random22Read@getunleash.io' }),
|
||||
);
|
||||
const user = await userStore.insert({
|
||||
name: 'Some User',
|
||||
email: 'random22Read@getunleash.io',
|
||||
});
|
||||
|
||||
await accessService.setUserRootRole(user.id, editorRole.id);
|
||||
await accessService.setUserRootRole(user.id, readRole.id);
|
||||
|
@ -6,7 +6,6 @@ const {
|
||||
AccessService,
|
||||
RoleName,
|
||||
} = require('../../../lib/services/access-service');
|
||||
const User = require('../../../lib/user');
|
||||
const { UPDATE_PROJECT } = require('../../../lib/permissions');
|
||||
const NotFoundError = require('../../../lib/error/notfound-error');
|
||||
|
||||
@ -20,9 +19,10 @@ let user;
|
||||
test.before(async () => {
|
||||
db = await dbInit('project_service_serial', getLogger);
|
||||
stores = db.stores;
|
||||
user = await stores.userStore.insert(
|
||||
new User({ name: 'Some Name', email: 'test@getunleash.io' }),
|
||||
);
|
||||
user = await stores.userStore.insert({
|
||||
name: 'Some Name',
|
||||
email: 'test@getunleash.io',
|
||||
});
|
||||
const config = { getLogger, experimental: { rbac: true } };
|
||||
accessService = new AccessService(stores, config);
|
||||
projectService = new ProjectService(stores, config, accessService);
|
||||
@ -240,12 +240,14 @@ test.serial('should add a member user to the project', async t => {
|
||||
};
|
||||
await projectService.createProject(project, user);
|
||||
|
||||
const projectMember1 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member', email: 'member1@getunleash.io' }),
|
||||
);
|
||||
const projectMember2 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member 2', email: 'member2@getunleash.io' }),
|
||||
);
|
||||
const projectMember1 = await stores.userStore.insert({
|
||||
name: 'Some Member',
|
||||
email: 'member1@getunleash.io',
|
||||
});
|
||||
const projectMember2 = await stores.userStore.insert({
|
||||
name: 'Some Member 2',
|
||||
email: 'member2@getunleash.io',
|
||||
});
|
||||
|
||||
const roles = await stores.accessStore.getRolesForProject(project.id);
|
||||
const memberRole = roles.find(r => r.name === RoleName.MEMBER);
|
||||
@ -271,12 +273,14 @@ test.serial('should add admin users to the project', async t => {
|
||||
};
|
||||
await projectService.createProject(project, user);
|
||||
|
||||
const projectAdmin1 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member', email: 'admin1@getunleash.io' }),
|
||||
);
|
||||
const projectAdmin2 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member 2', email: 'admin2@getunleash.io' }),
|
||||
);
|
||||
const projectAdmin1 = await stores.userStore.insert({
|
||||
name: 'Some Member',
|
||||
email: 'admin1@getunleash.io',
|
||||
});
|
||||
const projectAdmin2 = await stores.userStore.insert({
|
||||
name: 'Some Member 2',
|
||||
email: 'admin2@getunleash.io',
|
||||
});
|
||||
|
||||
const projectRoles = await stores.accessStore.getRolesForProject(
|
||||
project.id,
|
||||
@ -320,9 +324,10 @@ test.serial('add user should fail if user already have access', async t => {
|
||||
};
|
||||
await projectService.createProject(project, user);
|
||||
|
||||
const projectMember1 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member', email: 'member42@getunleash.io' }),
|
||||
);
|
||||
const projectMember1 = await stores.userStore.insert({
|
||||
name: 'Some Member',
|
||||
email: 'member42@getunleash.io',
|
||||
});
|
||||
|
||||
const roles = await stores.accessStore.getRolesForProject(project.id);
|
||||
const memberRole = roles.find(r => r.name === RoleName.MEMBER);
|
||||
@ -352,9 +357,10 @@ test.serial('should remove user from the project', async t => {
|
||||
};
|
||||
await projectService.createProject(project, user);
|
||||
|
||||
const projectMember1 = await stores.userStore.insert(
|
||||
new User({ name: 'Some Member', email: 'member99@getunleash.io' }),
|
||||
);
|
||||
const projectMember1 = await stores.userStore.insert({
|
||||
name: 'Some Member',
|
||||
email: 'member99@getunleash.io',
|
||||
});
|
||||
|
||||
const roles = await stores.accessStore.getRolesForProject(project.id);
|
||||
const memberRole = roles.find(r => r.name === RoleName.MEMBER);
|
||||
|
@ -6,7 +6,7 @@ import UserService from '../../../lib/services/user-service';
|
||||
import { AccessService } from '../../../lib/services/access-service';
|
||||
import NotFoundError from '../../../lib/error/notfound-error';
|
||||
import { EmailService } from '../../../lib/services/email-service';
|
||||
import User from '../../../lib/user';
|
||||
import User from '../../../lib/types/user';
|
||||
import { IUnleashConfig } from '../../../lib/types/option';
|
||||
import { createTestConfig } from '../../config/test-config';
|
||||
|
||||
|
@ -4,7 +4,7 @@ import getLogger from '../../fixtures/no-logger';
|
||||
import UserService from '../../../lib/services/user-service';
|
||||
import { AccessService, RoleName } from '../../../lib/services/access-service';
|
||||
import UserStore from '../../../lib/db/user-store';
|
||||
import User from '../../../lib/user';
|
||||
import User from '../../../lib/types/user';
|
||||
import { IRole } from '../../../lib/db/access-store';
|
||||
import ResetTokenService from '../../../lib/services/reset-token-service';
|
||||
import { EmailService } from '../../../lib/services/email-service';
|
||||
@ -51,7 +51,7 @@ test.serial('should create initial admin user', async t => {
|
||||
});
|
||||
|
||||
test.serial('should not be allowed to create existing user', async t => {
|
||||
await userStore.insert(new User({ username: 'test', name: 'Hans Mola' }));
|
||||
await userStore.insert({ username: 'test', name: 'Hans Mola' });
|
||||
await t.throwsAsync(
|
||||
userService.createUser({ username: 'test', rootRole: adminRole.id }),
|
||||
);
|
||||
|
@ -1,12 +1,6 @@
|
||||
'use strict';
|
||||
|
||||
const test = require('ava');
|
||||
const User = require('../../../lib/user');
|
||||
const {
|
||||
CREATE_FEATURE,
|
||||
DELETE_FEATURE,
|
||||
UPDATE_FEATURE,
|
||||
} = require('../../../lib/permissions');
|
||||
const NotFoundError = require('../../../lib/error/notfound-error');
|
||||
const dbInit = require('../helpers/database-init');
|
||||
const getLogger = require('../../fixtures/no-logger');
|
||||
@ -29,7 +23,7 @@ test.serial('should have no users', async t => {
|
||||
});
|
||||
|
||||
test.serial('should insert new user with email', async t => {
|
||||
const user = new User({ email: 'me2@mail.com' });
|
||||
const user = { email: 'me2@mail.com' };
|
||||
await stores.userStore.upsert(user);
|
||||
const users = await stores.userStore.getAll();
|
||||
t.deepEqual(users[0].email, user.email);
|
||||
@ -53,14 +47,14 @@ test.serial('should not allow two users with same email', async t => {
|
||||
});
|
||||
|
||||
test.serial('should insert new user with email and return it', async t => {
|
||||
const user = new User({ email: 'me2@mail.com' });
|
||||
const user = { email: 'me2@mail.com' };
|
||||
const newUser = await stores.userStore.upsert(user);
|
||||
t.deepEqual(newUser.email, user.email);
|
||||
t.truthy(newUser.id);
|
||||
});
|
||||
|
||||
test.serial('should insert new user with username', async t => {
|
||||
const user = new User({ username: 'admin' });
|
||||
const user = { username: 'admin' };
|
||||
await stores.userStore.upsert(user);
|
||||
const dbUser = await stores.userStore.get(user);
|
||||
t.deepEqual(dbUser.username, user.username);
|
||||
@ -79,7 +73,7 @@ test('Should require email or username', async t => {
|
||||
|
||||
test.serial('should set password_hash for user', async t => {
|
||||
const store = stores.userStore;
|
||||
const user = await store.insert(new User({ email: 'admin@mail.com' }));
|
||||
const user = await store.insert({ email: 'admin@mail.com' });
|
||||
await store.setPasswordHash(user.id, 'rubbish');
|
||||
const hash = await store.getPasswordHash(user.id);
|
||||
|
||||
@ -100,7 +94,7 @@ test.serial('should not get password_hash for unknown userId', async t => {
|
||||
|
||||
test.serial('should update loginAttempts for user', async t => {
|
||||
const store = stores.userStore;
|
||||
const user = new User({ email: 'admin@mail.com' });
|
||||
const user = { email: 'admin@mail.com' };
|
||||
await store.upsert(user);
|
||||
await store.incLoginAttempts(user);
|
||||
await store.incLoginAttempts(user);
|
||||
@ -111,9 +105,9 @@ test.serial('should update loginAttempts for user', async t => {
|
||||
|
||||
test.serial('should not increment for user unknwn user', async t => {
|
||||
const store = stores.userStore;
|
||||
const user = new User({ email: 'another@mail.com' });
|
||||
const user = { email: 'another@mail.com' };
|
||||
await store.upsert(user);
|
||||
await store.incLoginAttempts(new User({ email: 'unknown@mail.com' }));
|
||||
await store.incLoginAttempts({ email: 'unknown@mail.com' });
|
||||
const storedUser = await store.get(user);
|
||||
|
||||
t.is(storedUser.loginAttempts, 0);
|
||||
@ -121,9 +115,7 @@ test.serial('should not increment for user unknwn user', async t => {
|
||||
|
||||
test.serial('should reset user after successful login', async t => {
|
||||
const store = stores.userStore;
|
||||
const user = await store.insert(
|
||||
new User({ email: 'anotherWithResert@mail.com' }),
|
||||
);
|
||||
const user = await store.insert({ email: 'anotherWithResert@mail.com' });
|
||||
await store.incLoginAttempts(user);
|
||||
await store.incLoginAttempts(user);
|
||||
|
||||
@ -134,37 +126,20 @@ test.serial('should reset user after successful login', async t => {
|
||||
t.true(storedUser.seenAt >= user.seenAt);
|
||||
});
|
||||
|
||||
test.serial('should store and get permissions', async t => {
|
||||
const store = stores.userStore;
|
||||
const email = 'userWithPermissions@mail.com';
|
||||
const user = new User({
|
||||
email,
|
||||
permissions: [CREATE_FEATURE, UPDATE_FEATURE, DELETE_FEATURE],
|
||||
});
|
||||
|
||||
await store.upsert(user);
|
||||
|
||||
const storedUser = await store.get({ email });
|
||||
|
||||
t.deepEqual(storedUser.permissions, user.permissions);
|
||||
});
|
||||
|
||||
test.serial('should only update specified fields on user', async t => {
|
||||
const store = stores.userStore;
|
||||
const email = 'userTobeUpdated@mail.com';
|
||||
const user = new User({
|
||||
const user = {
|
||||
email,
|
||||
username: 'test',
|
||||
permissions: [CREATE_FEATURE, UPDATE_FEATURE, DELETE_FEATURE],
|
||||
});
|
||||
};
|
||||
|
||||
await store.upsert(user);
|
||||
|
||||
await store.upsert({ username: 'test', permissions: [CREATE_FEATURE] });
|
||||
await store.upsert({ username: 'test' });
|
||||
|
||||
const storedUser = await store.get({ email });
|
||||
|
||||
t.deepEqual(storedUser.email, user.email);
|
||||
t.deepEqual(storedUser.username, user.username);
|
||||
t.deepEqual(storedUser.permissions, [CREATE_FEATURE]);
|
||||
});
|
||||
|
2
src/test/fixtures/access-service-mock.ts
vendored
2
src/test/fixtures/access-service-mock.ts
vendored
@ -8,7 +8,7 @@ import {
|
||||
IPermission,
|
||||
IRoleData,
|
||||
} from '../../lib/services/access-service';
|
||||
import User from '../../lib/user';
|
||||
import User from '../../lib/types/user';
|
||||
import noLoggerProvider from './no-logger';
|
||||
|
||||
class AccessServiceMock extends AccessService {
|
||||
|
2
src/test/fixtures/fake-user-store.ts
vendored
2
src/test/fixtures/fake-user-store.ts
vendored
@ -1,5 +1,5 @@
|
||||
import UserStore, { IUserLookup } from '../../lib/db/user-store';
|
||||
import User from '../../lib/user';
|
||||
import User from '../../lib/types/user';
|
||||
import noLoggerProvider from './no-logger';
|
||||
|
||||
class UserStoreMock extends UserStore {
|
||||
|
Loading…
Reference in New Issue
Block a user