Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(editor): Add routing middleware, permission checks, RBAC store, RBAC component #7702

Merged
merged 27 commits into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
ea4123a
feat: add routing middleware, rbac store, rbac component
alexgrozav Nov 13, 2023
25bb007
test: add unit tests for rbac functionality
alexgrozav Nov 14, 2023
0a334c2
feat: replace existing isAuthorized permissions check
alexgrozav Nov 15, 2023
f43648b
test: add tests for permission checks
alexgrozav Nov 15, 2023
21501ef
chore: merge master
alexgrozav Nov 15, 2023
e8b6123
fix: fix /users route permission
alexgrozav Nov 15, 2023
659d93d
feat: code improvements and test coverage increase
alexgrozav Nov 15, 2023
ebb6645
chore: add intermediary const
alexgrozav Nov 15, 2023
449a219
fix: fix tag scope name
alexgrozav Nov 15, 2023
2be9594
fix: remove .only
alexgrozav Nov 15, 2023
14bc6f2
Merge remote-tracking branch 'origin/master' into pay-1005-front-end-…
cstuncsik Nov 16, 2023
4229f24
Merge remote-tracking branch 'origin/master' into pay-1005-front-end-…
cstuncsik Nov 16, 2023
e77a9bd
Merge remote-tracking branch 'origin/master' into pay-1005-front-end-…
cstuncsik Nov 17, 2023
2e918c6
fix: Update permission package
cstuncsik Nov 17, 2023
b57306e
fix: Add RBAC store to permissions
cstuncsik Nov 20, 2023
edb3679
fix: Remove unnecessary permission setting
cstuncsik Nov 20, 2023
04f4c17
fix: Update variables view unit test
cstuncsik Nov 20, 2023
5493903
chore: merge master
alexgrozav Nov 21, 2023
6a6ffef
feat: add init functions
alexgrozav Nov 21, 2023
e93c2f2
fix: Update permissions
cstuncsik Nov 21, 2023
0bd74d3
Merge branch 'pay-1005-front-end-permissions-overhaul' of github.com:…
cstuncsik Nov 21, 2023
ddeeb25
fix: Remove unused import
cstuncsik Nov 21, 2023
fdc3d09
fix: Add back user to variable view test
cstuncsik Nov 21, 2023
34eb0d2
fix: Roll back variable view changes
cstuncsik Nov 21, 2023
6d7c77e
fix: Update permissions
cstuncsik Nov 21, 2023
d403b14
fix: Permission object is not optional in test function
cstuncsik Nov 22, 2023
1eba640
Merge remote-tracking branch 'origin/master' into pay-1005-front-end-…
cstuncsik Nov 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/design-system/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ import {
N8nUserStack,
} from './components';

export const N8nPlugin: Plugin<{}> = {
export interface N8nPluginOptions {}

export const N8nPlugin: Plugin<N8nPluginOptions> = {
install: (app) => {
app.component('n8n-action-box', N8nActionBox);
app.component('n8n-action-dropdown', N8nActionDropdown);
Expand Down
1 change: 1 addition & 0 deletions packages/editor-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@jsplumb/util": "^5.13.2",
"@lezer/common": "^1.0.4",
"@n8n/codemirror-lang-sql": "^1.0.2",
"@n8n/permissions": "workspace:^",
"@vueuse/components": "^10.5.0",
"@vueuse/core": "^10.5.0",
"axios": "^0.21.1",
Expand Down
61 changes: 61 additions & 0 deletions packages/editor-ui/src/components/RBAC.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<script lang="ts">
import type { PropType } from 'vue';
import { computed, defineComponent } from 'vue';
import { useRBACStore } from '@/stores/rbac.store';
import type { HasScopeMode, Scope, Resource } from '@n8n/permissions';
import {
inferProjectIdFromRoute,
inferResourceIdFromRoute,
inferResourceTypeFromRoute,
} from '@/utils/rbacUtils';
import { useRoute } from 'vue-router';

export default defineComponent({
props: {
scope: {
type: [String, Array] as PropType<Scope | Scope[]>,
required: true,
},
mode: {
type: String as PropType<HasScopeMode>,
default: 'allOf',
},
resourceType: {
type: String as PropType<Resource>,
default: undefined,
},
resourceId: {
type: String,
default: undefined,
},
projectId: {
type: String,
default: undefined,
},
},
setup(props, { slots }) {
const rbacStore = useRBACStore();
const route = useRoute();

const hasScope = computed(() => {
const projectId = props.projectId ?? inferProjectIdFromRoute(route);
const resourceType = props.resourceType ?? inferResourceTypeFromRoute(route);
const resourceId = resourceType
? props.resourceId ?? inferResourceIdFromRoute(route)
: undefined;

return rbacStore.hasScope(
props.scope,
{
projectId,
resourceType,
resourceId,
},
{ mode: props.mode },
);
});

return () => (hasScope.value ? slots.default?.() : slots.fallback?.());
},
});
</script>
19 changes: 19 additions & 0 deletions packages/editor-ui/src/middleware/authenticated.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useUsersStore } from '@/stores';
import type { RouterMiddleware } from '@/types/router';
import { VIEWS } from '@/constants';

export type AuthenticatedMiddlewareOptions = {};

export const authenticated: RouterMiddleware<AuthenticatedMiddlewareOptions> = async (
to,
from,
next,
) => {
const usersStore = useUsersStore();
const redirect =
to.query.redirect ?? encodeURIComponent(`${window.location.pathname}${window.location.search}`);

if (!usersStore.currentUser) {
return next({ name: VIEWS.SIGNIN, query: { redirect } });
}
};
30 changes: 30 additions & 0 deletions packages/editor-ui/src/middleware/enterprise.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { RouterMiddleware } from '@/types/router';
import { useSettingsStore } from '@/stores';
import type { EnterpriseEditionFeature } from '@/constants';
import { VIEWS } from '@/constants';

export type EnterpriseMiddlewareOptions = {
features: EnterpriseEditionFeature[];
mode?: 'oneOf' | 'allOf';
};

export const enterprise: RouterMiddleware<EnterpriseMiddlewareOptions> = async (
to,
from,
next,
options,
) => {
const settingsStore = useSettingsStore();
const mode = options.mode ?? 'allOf';

let valid: boolean;
if (mode === 'allOf') {
valid = options.features.every((feature) => settingsStore.isEnterpriseFeatureEnabled(feature));
Copy link
Contributor

@cstuncsik cstuncsik Nov 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a nitpick (coming from my functional programming self :))
it could be pointfree (same below)

Suggested change
valid = options.features.every((feature) => settingsStore.isEnterpriseFeatureEnabled(feature));
valid = options.features.every(settingsStore.isEnterpriseFeatureEnabled);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

} else {
valid = options.features.some((feature) => settingsStore.isEnterpriseFeatureEnabled(feature));
}

if (!valid) {
return next({ name: VIEWS.HOMEPAGE });
}
};
18 changes: 18 additions & 0 deletions packages/editor-ui/src/middleware/guest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useUsersStore } from '@/stores';
import type { RouterMiddleware } from '@/types/router';
import { VIEWS } from '@/constants';

export type GuestMiddlewareOptions = {};

export const guest: RouterMiddleware<GuestMiddlewareOptions> = async (to, from, next) => {
const usersStore = useUsersStore();
const redirect = to.query.redirect as string;

if (usersStore.currentUser) {
if (redirect && redirect.startsWith('/')) {
next(redirect);
}

return next({ name: VIEWS.HOMEPAGE });
}
};
12 changes: 12 additions & 0 deletions packages/editor-ui/src/middleware/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import type { RouterMiddlewareType, RouterMiddleware } from '@/types/router';
import { authenticated } from './authenticated';
import { enterprise } from './enterprise';
import { guest } from './guest';
import { rbac } from './rbac';

export const middleware: Record<RouterMiddlewareType, RouterMiddleware> = {
authenticated,
enterprise,
guest,
rbac,
};
41 changes: 41 additions & 0 deletions packages/editor-ui/src/middleware/rbac.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { RouterMiddleware } from '@/types/router';
import { VIEWS } from '@/constants';
import type { HasScopeOptions, Scope } from '@n8n/permissions';
import { useRBACStore } from '@/stores/rbac.store';
import {
inferProjectIdFromRoute,
inferResourceIdFromRoute,
inferResourceTypeFromRoute,
} from '@/utils/rbacUtils';

export type RBACMiddlewareOptions = {
scope: Scope | Scope[];
options?: HasScopeOptions;
};

export const rbac: RouterMiddleware<RBACMiddlewareOptions> = async (
to,
from,
next,
{ scope, options },
) => {
const rbacStore = useRBACStore();

const projectId = inferProjectIdFromRoute(to);
const resourceType = inferResourceTypeFromRoute(to);
const resourceId = resourceType ? inferResourceIdFromRoute(to) : undefined;

const valid = rbacStore.hasScope(
scope,
{
projectId,
resourceType,
resourceId,
},
options,
);

if (!valid) {
return next({ name: VIEWS.HOMEPAGE });
}
};
5 changes: 5 additions & 0 deletions packages/editor-ui/src/permissions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import type { IUser, ICredentialsResponse, IWorkflowDb } from '@/Interface';
import { EnterpriseEditionFeature, PLACEHOLDER_EMPTY_WORKFLOW_ID } from '@/constants';
import { useSettingsStore } from './stores/settings.store';

/**
* Old permissions implementation
* @deprecated
*/

export const enum UserRole {
InstanceOwner = 'isInstanceOwner',
ResourceOwner = 'isOwner',
Expand Down
2 changes: 2 additions & 0 deletions packages/editor-ui/src/plugins/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@ import ElementPlus, { ElLoading, ElMessageBox } from 'element-plus';
import { N8nPlugin } from 'n8n-design-system';
import { useMessage } from '@/composables/useMessage';
import EnterpriseEdition from '@/components/EnterpriseEdition.ee.vue';
import RBAC from '@/components/RBAC.vue';

export const GlobalComponentsPlugin: Plugin<{}> = {
install(app) {
const messageService = useMessage();

app.component('enterprise-edition', EnterpriseEdition);
app.component('RBAC', RBAC);

app.use(ElementPlus);
app.use(N8nPlugin);
Expand Down
Loading
Loading