Skip to content

Commit

Permalink
Merge branch 'master' into definitely-delete-restAPI-mixin
Browse files Browse the repository at this point in the history
* master:
  refactor: Async functions don't need to explicitly return promises (no-changelog) (#6041)
  SSO/SAML : add Base URL to redirects in acsHandler (#5923)
  refactor: Integrate `consistent-type-imports` in FE packages (no-changelog) (#6060)
  fix(core): Skip license activation when instance was already activated (#6064)
  refactor(core): Setup decorator based RBAC (no-changelog) (#5787)

# Conflicts:
#	packages/editor-ui/src/mixins/restApi.ts
  • Loading branch information
MiloradFilipovic committed Apr 24, 2023
2 parents a4df8a6 + 308a943 commit b3e75bd
Show file tree
Hide file tree
Showing 317 changed files with 913 additions and 941 deletions.
5 changes: 5 additions & 0 deletions packages/@n8n_io/eslint-config/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,11 @@ const config = (module.exports = {
*/
'@typescript-eslint/consistent-type-assertions': 'error',

/**
* https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-imports.md
*/
'@typescript-eslint/consistent-type-imports': 'error',

/**
* https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/member-delimiter-style.md
*/
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ module.exports = {
],

rules: {
'@typescript-eslint/consistent-type-imports': 'error',

// TODO: Remove this
'import/no-cycle': 'warn',
'import/order': 'off',
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/CredentialsHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -542,10 +542,10 @@ export class CredentialsHelper extends ICredentialsHelper {
): Promise<INodeCredentialTestResult> {
const credentialTestFunction = this.getCredentialTestFunction(credentialType);
if (credentialTestFunction === undefined) {
return Promise.resolve({
return {
status: 'Error',
message: 'No testing function found for this credential.',
});
};
}

if (credentialsDecrypted.data) {
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/InternalHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,12 @@ export class InternalHooks implements IInternalHooksClass {
runData?: IRun,
userId?: string,
): Promise<void> {
const promises = [Promise.resolve()];

if (!workflow.id) {
return Promise.resolve();
return;
}

const promises = [];

const properties: IExecutionTrackProperties = {
workflow_id: workflow.id,
is_manual: false,
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/Ldap/LdapService.ee.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class LdapService {
await this.client.unbind();
return searchEntries;
}
return Promise.resolve([]);
return [];
}

/**
Expand Down
24 changes: 0 additions & 24 deletions packages/cli/src/UserManagement/UserManagementHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,30 +196,6 @@ export async function getUserById(userId: string): Promise<User> {
return user;
}

/**
* Check if a URL contains an auth-excluded endpoint.
*/
export function isAuthExcluded(url: string, ignoredEndpoints: Readonly<string[]>): boolean {
return !!ignoredEndpoints
.filter(Boolean) // skip empty paths
.find((ignoredEndpoint) => url.startsWith(`/${ignoredEndpoint}`));
}

/**
* Check if the endpoint is `POST /users/:id`.
*/
export function isPostUsersId(req: express.Request, restEndpoint: string): boolean {
return (
req.method === 'POST' &&
new RegExp(`/${restEndpoint}/users/[\\w\\d-]*`).test(req.url) &&
!req.url.includes('reinvite')
);
}

export function isAuthenticatedRequest(request: express.Request): request is AuthenticatedRequest {
return request.user !== undefined;
}

// ----------------------------------
// hashing
// ----------------------------------
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/commands/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,16 @@ export class Start extends BaseCommand {
await license.init(this.instanceId);

const activationKey = config.getEnv('license.activationKey');

if (activationKey) {
const hasCert = (await license.loadCertStr()).length > 0;

if (hasCert) {
return LoggerProxy.debug('Skipping license activation');
}

try {
LoggerProxy.debug('Attempting license activation');
await license.activate(activationKey);
} catch (e) {
LoggerProxy.error('Could not activate license', e as Error);
Expand Down
6 changes: 2 additions & 4 deletions packages/cli/src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import validator from 'validator';
import { Get, Post, RestController } from '@/decorators';
import { Authorized, Get, Post, RestController } from '@/decorators';
import { AuthError, BadRequestError, InternalServerError } from '@/ResponseHelper';
import { sanitizeUser, withFeatureFlags } from '@/UserManagement/UserManagementHelper';
import { issueCookie, resolveJwt } from '@/auth/jwt';
Expand Down Expand Up @@ -58,7 +58,6 @@ export class AuthController {

/**
* Log in a user.
* Authless endpoint.
*/
@Post('/login')
async login(req: LoginRequest, res: Response): Promise<PublicUser | undefined> {
Expand Down Expand Up @@ -135,7 +134,6 @@ export class AuthController {

/**
* Validate invite token to enable invitee to set up their account.
* Authless endpoint.
*/
@Get('/resolve-signup-token')
async resolveSignupToken(req: UserRequest.ResolveSignUp) {
Expand Down Expand Up @@ -196,8 +194,8 @@ export class AuthController {

/**
* Log out a user.
* Authless endpoint.
*/
@Authorized()
@Post('/logout')
logout(req: Request, res: Response) {
res.clearCookie(AUTH_COOKIE_NAME);
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/controllers/ldap.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pick from 'lodash.pick';
import { Get, Post, Put, RestController } from '@/decorators';
import { Authorized, Get, Post, Put, RestController } from '@/decorators';
import { getLdapConfig, getLdapSynchronizations, updateLdapConfig } from '@/Ldap/helpers';
import { LdapService } from '@/Ldap/LdapService.ee';
import { LdapSync } from '@/Ldap/LdapSync.ee';
Expand All @@ -8,6 +8,7 @@ import { BadRequestError } from '@/ResponseHelper';
import { NON_SENSIBLE_LDAP_CONFIG_PROPERTIES } from '@/Ldap/constants';
import { InternalHooks } from '@/InternalHooks';

@Authorized(['global', 'owner'])
@RestController('/ldap')
export class LdapController {
constructor(
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/controllers/me.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import validator from 'validator';
import { plainToInstance } from 'class-transformer';
import { Delete, Get, Patch, Post, RestController } from '@/decorators';
import { Authorized, Delete, Get, Patch, Post, RestController } from '@/decorators';
import {
compareHash,
hashPassword,
Expand Down Expand Up @@ -30,6 +30,7 @@ import { randomBytes } from 'crypto';
import { isSamlLicensedAndEnabled } from '../sso/saml/samlHelpers';
import { UserService } from '@/user/user.service';

@Authorized()
@RestController('/me')
export class MeController {
private readonly logger: ILogger;
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/controllers/nodeTypes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ import { readFile } from 'fs/promises';
import get from 'lodash.get';
import { Request } from 'express';
import type { INodeTypeDescription, INodeTypeNameVersion } from 'n8n-workflow';
import { Post, RestController } from '@/decorators';
import { Authorized, Post, RestController } from '@/decorators';
import { getNodeTranslationPath } from '@/TranslationHelpers';
import type { Config } from '@/config';
import type { NodeTypes } from '@/NodeTypes';

@Authorized()
@RestController('/node-types')
export class NodeTypesController {
private readonly config: Config;
Expand Down
12 changes: 2 additions & 10 deletions packages/cli/src/controllers/nodes.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
STARTER_TEMPLATE_NAME,
UNKNOWN_FAILURE_REASON,
} from '@/constants';
import { Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { NodeRequest } from '@/requests';
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
import {
Expand All @@ -30,10 +30,10 @@ import { LoadNodesAndCredentials } from '@/LoadNodesAndCredentials';
import { InternalHooks } from '@/InternalHooks';
import { Push } from '@/push';
import { Config } from '@/config';
import { isAuthenticatedRequest } from '@/UserManagement/UserManagementHelper';

const { PACKAGE_NOT_INSTALLED, PACKAGE_NAME_NOT_PROVIDED } = RESPONSE_ERROR_MESSAGES;

@Authorized(['global', 'owner'])
@RestController('/nodes')
export class NodesController {
constructor(
Expand All @@ -43,14 +43,6 @@ export class NodesController {
private internalHooks: InternalHooks,
) {}

// TODO: move this into a new decorator `@Authorized`
@Middleware()
checkIfOwner(req: Request, res: Response, next: NextFunction) {
if (!isAuthenticatedRequest(req) || req.user.globalRole.name !== 'owner')
res.status(403).json({ status: 'error', message: 'Unauthorized' });
else next();
}

// TODO: move this into a new decorator `@IfConfig('executions.mode', 'queue')`
@Middleware()
checkIfCommunityNodesEnabled(req: Request, res: Response, next: NextFunction) {
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/controllers/owner.controller.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import validator from 'validator';
import { validateEntity } from '@/GenericHelpers';
import { Get, Post, RestController } from '@/decorators';
import { Authorized, Get, Post, RestController } from '@/decorators';
import { BadRequestError } from '@/ResponseHelper';
import {
hashPassword,
Expand All @@ -20,6 +20,7 @@ import type {
WorkflowRepository,
} from '@db/repositories';

@Authorized(['global', 'owner'])
@RestController('/owner')
export class OwnerController {
private readonly config: Config;
Expand Down
3 changes: 0 additions & 3 deletions packages/cli/src/controllers/passwordReset.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export class PasswordResetController {

/**
* Send a password reset email.
* Authless endpoint.
*/
@Post('/forgot-password')
async forgotPassword(req: PasswordResetRequest.Email) {
Expand Down Expand Up @@ -171,7 +170,6 @@ export class PasswordResetController {

/**
* Verify password reset token and user ID.
* Authless endpoint.
*/
@Get('/resolve-password-token')
async resolvePasswordToken(req: PasswordResetRequest.Credentials) {
Expand Down Expand Up @@ -213,7 +211,6 @@ export class PasswordResetController {

/**
* Verify password reset token and user ID and update password.
* Authless endpoint.
*/
@Post('/change-password')
async changePassword(req: PasswordResetRequest.NewPassword, res: Response) {
Expand Down
13 changes: 4 additions & 9 deletions packages/cli/src/controllers/tags.controller.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Request, Response, NextFunction } from 'express';
import type { Config } from '@/config';
import { Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import { Authorized, Delete, Get, Middleware, Patch, Post, RestController } from '@/decorators';
import type { IDatabaseCollections, IExternalHooksClass, ITagWithCountDb } from '@/Interfaces';
import { TagEntity } from '@db/entities/TagEntity';
import type { TagRepository } from '@db/repositories';
import { validateEntity } from '@/GenericHelpers';
import { BadRequestError, UnauthorizedError } from '@/ResponseHelper';
import { BadRequestError } from '@/ResponseHelper';
import { TagsRequest } from '@/requests';

@Authorized()
@RestController('/tags')
export class TagsController {
private config: Config;
Expand Down Expand Up @@ -91,15 +92,9 @@ export class TagsController {
return tag;
}

@Authorized(['global', 'owner'])
@Delete('/:id(\\d+)')
async deleteTag(req: TagsRequest.Delete) {
const isInstanceOwnerSetUp = this.config.getEnv('userManagement.isInstanceOwnerSetUp');
if (isInstanceOwnerSetUp && req.user.globalRole.name !== 'owner') {
throw new UnauthorizedError(
'You are not allowed to perform this action',
'Only owners can remove tags',
);
}
const { id } = req.params;
await this.externalHooks.run('tag.beforeDelete', [id]);

Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/controllers/translation.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { Request } from 'express';
import { ICredentialTypes } from 'n8n-workflow';
import { join } from 'path';
import { access } from 'fs/promises';
import { Get, RestController } from '@/decorators';
import { Authorized, Get, RestController } from '@/decorators';
import { BadRequestError, InternalServerError } from '@/ResponseHelper';
import { Config } from '@/config';
import { NODES_BASE_DIR } from '@/constants';
Expand All @@ -14,6 +14,7 @@ export declare namespace TranslationRequest {
export type Credential = Request<{}, {}, {}, { credentialType: string }>;
}

@Authorized()
@RestController('/')
export class TranslationController {
constructor(private config: Config, private credentialTypes: ICredentialTypes) {}
Expand Down
5 changes: 4 additions & 1 deletion packages/cli/src/controllers/users.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ErrorReporterProxy as ErrorReporter } from 'n8n-workflow';
import { User } from '@db/entities/User';
import { SharedCredentials } from '@db/entities/SharedCredentials';
import { SharedWorkflow } from '@db/entities/SharedWorkflow';
import { Delete, Get, Post, RestController } from '@/decorators';
import { Authorized, NoAuthRequired, Delete, Get, Post, RestController } from '@/decorators';
import {
addInviteLinkToUser,
generateUserInviteUrl,
Expand Down Expand Up @@ -41,6 +41,7 @@ import type {
UserRepository,
} from '@db/repositories';

@Authorized(['global', 'owner'])
@RestController('/users')
export class UsersController {
private config: Config;
Expand Down Expand Up @@ -282,6 +283,7 @@ export class UsersController {
/**
* Fill out user shell with first name, last name, and password.
*/
@NoAuthRequired()
@Post('/:id')
async updateUser(req: UserRequest.Update, res: Response) {
const { id: inviteeId } = req.params;
Expand Down Expand Up @@ -343,6 +345,7 @@ export class UsersController {
return withFeatureFlags(this.postHog, sanitizeUser(updatedUser));
}

@Authorized('any')
@Get('/')
async listUsers(req: UserRequest.List) {
const users = await this.userRepository.find({ relations: ['globalRole', 'authIdentities'] });
Expand Down
16 changes: 16 additions & 0 deletions packages/cli/src/decorators/Authorized.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* eslint-disable @typescript-eslint/ban-types */
/* eslint-disable @typescript-eslint/naming-convention */
import { CONTROLLER_AUTH_ROLES } from './constants';
import type { AuthRoleMetadata } from './types';

export function Authorized(authRole: AuthRoleMetadata[string] = 'any'): Function {
return function (target: Function | Object, handlerName?: string) {
const controllerClass = handlerName ? target.constructor : target;
const authRoles = (Reflect.getMetadata(CONTROLLER_AUTH_ROLES, controllerClass) ??
{}) as AuthRoleMetadata;
authRoles[handlerName ?? '*'] = authRole;
Reflect.defineMetadata(CONTROLLER_AUTH_ROLES, authRoles, controllerClass);
};
}

export const NoAuthRequired = () => Authorized('none');
1 change: 1 addition & 0 deletions packages/cli/src/decorators/constants.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const CONTROLLER_ROUTES = 'CONTROLLER_ROUTES';
export const CONTROLLER_BASE_PATH = 'CONTROLLER_BASE_PATH';
export const CONTROLLER_MIDDLEWARES = 'CONTROLLER_MIDDLEWARES';
export const CONTROLLER_AUTH_ROLES = 'CONTROLLER_AUTH_ROLES';
1 change: 1 addition & 0 deletions packages/cli/src/decorators/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { Authorized, NoAuthRequired } from './Authorized';
export { RestController } from './RestController';
export { Get, Post, Put, Patch, Delete } from './Route';
export { Middleware } from './Middleware';
Expand Down
Loading

0 comments on commit b3e75bd

Please sign in to comment.