Skip to content

Commit

Permalink
Merge pull request #369 from frontegg/FR-13901-rule-based-entitlements
Browse files Browse the repository at this point in the history
  • Loading branch information
doregg authored Nov 1, 2023
2 parents 43a976a + 903eecf commit 4b00086
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 22 deletions.
69 changes: 58 additions & 11 deletions packages/vue/src/auth/entitlements.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @ts-ignore
import { inject, computed } from 'vue';
import { EntitledToOptions, Entitlement } from '@frontegg/types';
import { EntitledToOptions, Entitlement, CustomAttributes, Attributes, JwtAttributes } from '@frontegg/types';

import {
getFeatureEntitlements,
Expand All @@ -9,36 +9,83 @@ import {
AuthState,
} from '@frontegg/redux-store';

import { USE_ENTITLEMENTS_V2_ENDPOINT_FF } from '@frontegg/rest-api';

import { authStateKey } from '../constants';
import { useFeatureFlag } from '../auth/mapAuthState';

const getEntitlementsState = () => {
/**
* @returns user state
*/
const useGetUserState = () => {
const authState = inject(authStateKey) as AuthState;
return authState.user?.entitlements as any;
return authState.user;
};

/**
* @returns true when need to use entitlements V2 API
*/
const useIsV2API = () => {
const [useEntitlementsV2] = useFeatureFlag([USE_ENTITLEMENTS_V2_ENDPOINT_FF]);
return useEntitlementsV2;
};

/**
* @param customAttributes consumer attributes
* @returns is entitled query data including: entitltments state, final attributes (consumer and frontegg) and API version to use
*/
const useEntitlementsQueryData = (customAttributes?: CustomAttributes) => {
const user = useGetUserState();
const entitlements = user?.entitlements;
const isV2 = useIsV2API();

const attributes: Attributes = {
custom: customAttributes,
jwt: user as JwtAttributes | undefined,
};

return {
entitlements,
attributes,
isV2
};
};

/**
@param key feature
@param key feature key
@param customAttributes consumer attributes
@returns if the user is entitled to the given feature. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
export const useFeatureEntitlements = (key: string): Entitlement => {
return computed(() => getFeatureEntitlements(getEntitlementsState(), key));
export const useFeatureEntitlements = (key: string, customAttributes?: CustomAttributes): Entitlement => {
return computed(() => {
const { entitlements, attributes, isV2 } = useEntitlementsQueryData(customAttributes);
return getFeatureEntitlements(entitlements, key, attributes, isV2)
});
};

/**
@param key permission
@param key permission key
@param customAttributes consumer attributes
@returns if the user is entitled to the given permission. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
export const usePermissionEntitlements = (key: string): Entitlement => {
return computed(() => getPermissionEntitlements(getEntitlementsState(), key));
export const usePermissionEntitlements = (key: string, customAttributes?: CustomAttributes): Entitlement => {
return computed(() => {
const { entitlements, attributes, isV2 } = useEntitlementsQueryData(customAttributes);
return getPermissionEntitlements(entitlements, key, attributes, isV2)
});
};

/**
@param entitledToOptions - including permission or feature key
@param customAttributes consumer attributes
@returns if the user is entitled to the given permission or feature. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
export const useEntitlements = (entitledToOptions: EntitledToOptions): Entitlement => {
return computed(() => getEntitlements(getEntitlementsState(), entitledToOptions));
export const useEntitlements = (entitledToOptions: EntitledToOptions, customAttributes?: CustomAttributes): Entitlement => {
return computed(() => {
const { entitlements, attributes, isV2 } = useEntitlementsQueryData(customAttributes);
return getEntitlements(entitlements as any, entitledToOptions, attributes, isV2)
});
};
6 changes: 6 additions & 0 deletions packages/vue/src/auth/mapAuthState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// @ts-ignore
import { inject, onUpdated, onMounted, reactive, onBeforeUnmount, computed } from 'vue';
import { FeatureFlags } from '@frontegg/rest-api';
import { defaultGetterGenerator, objectMappers } from '../helpers';
import {
AcceptInvitationActions,
Expand Down Expand Up @@ -154,6 +155,11 @@ export const useLoadEntitlements = () => {
return inject(loadEntitlementsKey);
};

export const useFeatureFlag = (keys: string[]) => {
const { appName } = useFronteggStore();
return FeatureFlags.getFeatureFlags(keys, appName);
};

export const useFrontegg = () => {
const fronteggLoaded = useFronteggLoaded();
const unsubscribeFronteggStore = useUnsubscribeFronteggStore();
Expand Down
10 changes: 5 additions & 5 deletions packages/vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { AdminPortal, initialize } from '@frontegg/js';
import { FronteggAuthService } from './auth/service';
import { connectMapState, connectFronteggStoreV3 } from './auth/mapAuthState';
import { ContextHolder, FronteggFrameworks } from '@frontegg/rest-api';
import { EntitledToOptions } from '@frontegg/types';
import { EntitledToOptions, CustomAttributes } from '@frontegg/types';
import {
authStateKey,
fronteggAuthKey,
Expand Down Expand Up @@ -243,10 +243,10 @@ const Frontegg: PluginObject<PluginOptions> | any = {
this.fronteggAuth = Vue.fronteggAuth;
this.loginWithRedirect = loginWithRedirect.bind(this);

// _entitlements was added for to make the computed property reactive, then it will get updated
this.getFeatureEntitlements = (_entitlements: User['entitlements'], key: string) => fronteggApp.getFeatureEntitlements(key);
this.getPermissionEntitlements = (_entitlements: User['entitlements'], key: string) => fronteggApp.getPermissionEntitlements(key);
this.getEntitlements = (_entitlements: User['entitlements'], entitledToOptions: EntitledToOptions) => fronteggApp.getEntitlements(entitledToOptions);
// _user was added for to make the computed property reactive, then it will get updated
this.getFeatureEntitlements = (_user: User | undefined | null, key: string, customAttributes?: CustomAttributes) => fronteggApp.getFeatureEntitlements(key, customAttributes);
this.getPermissionEntitlements = (_user: User | undefined | null, key: string, customAttributes?: CustomAttributes) => fronteggApp.getPermissionEntitlements(key, customAttributes);
this.getEntitlements = (_user: User | undefined | null, entitledToOptions: EntitledToOptions, customAttributes?: CustomAttributes) => fronteggApp.getEntitlements(entitledToOptions, customAttributes);
this.loadEntitlements = loadEntitlements;

connectMapState(this);
Expand Down
15 changes: 9 additions & 6 deletions packages/vue/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EnhancedStore } from '@reduxjs/toolkit';
import { Unsubscribe } from 'redux';
import { Entitlement, User } from '@frontegg/redux-store';
import { EntitledToOptions, LoadEntitlementsCallback } from '@frontegg/types';
import { EntitledToOptions, LoadEntitlementsCallback, CustomAttributes } from '@frontegg/types';
import { FronteggPluginService } from './interfaces';

declare module 'vue/types/vue' {
Expand All @@ -20,25 +20,28 @@ declare module 'vue/types/vue' {
loginWithRedirect: () => void;

/**
@param key feature
@param key feature key
@param customAttributes consumer attributes
@returns if the user is entitled to the given feature. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
getFeatureEntitlements: (_entitlements: User['entitlements'], key: string) => Entitlement;
getFeatureEntitlements: (_user: User | undefined | null, key: string, customAttributes?: CustomAttributes) => Entitlement;

/**
@param key permission
@param key permission key
@param customAttributes consumer attributes
@returns if the user is entitled to the given permission. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
getPermissionEntitlements: (_entitlements: User['entitlements'], key: string) => Entitlement;
getPermissionEntitlements: (_user: User | undefined | null, key: string, customAttributes?: CustomAttributes) => Entitlement;

/**
@param entitledToOptions - including permission or feature key
@param customAttributes consumer attributes
@returns if the user is entitled to the given permission or feature. Attaching the justification if not
@throws when entitlement is not enabled via frontegg options
*/
getEntitlements: (_entitlements: User['entitlements'], entitledToOptions: EntitledToOptions) => Entitlement;
getEntitlements: (_user: User | undefined | null, entitledToOptions: EntitledToOptions, customAttributes?: CustomAttributes) => Entitlement;

/**
* Load entitlements
Expand Down

0 comments on commit 4b00086

Please sign in to comment.