Browse Source

Implemented support for multiple policies

jnfrati 3 years ago
parent
commit
658e692e3f

+ 13 - 8
dashboard/src/shared/auth/AuthContext.tsx

@@ -5,14 +5,14 @@ import { POLICY_HIERARCHY_TREE, populatePolicy } from "./authorization-helpers";
 import { PolicyDocType } from "./types";
 
 type AuthContext = {
-  currentPolicy: PolicyDocType;
+  currentPolicy: PolicyDocType[];
 };
 
 export const AuthContext = React.createContext<AuthContext>({} as AuthContext);
 
 const AuthProvider: React.FC = ({ children }) => {
   const { user, currentProject } = useContext(Context);
-  const [currentPolicy, setCurrentPolicy] = useState(null);
+  const [currentPolicy, setCurrentPolicy] = useState<PolicyDocType[]>(null);
 
   useEffect(() => {
     let isSubscribed = true;
@@ -20,20 +20,25 @@ const AuthProvider: React.FC = ({ children }) => {
       setCurrentPolicy(null);
     } else {
       api
-        .getPolicyDocument("<token>", {}, { project_id: currentProject?.id })
+        .getPolicyDocument<PolicyDocType[]>(
+          "<token>",
+          {},
+          { project_id: currentProject?.id }
+        )
         .then((res) => {
           if (!isSubscribed) {
             return;
           }
-          const currentPolicy = res.data[0];
-          setCurrentPolicy(
+
+          const policies = res.data.map((incompletePolicy) =>
             populatePolicy(
-              currentPolicy,
+              incompletePolicy,
               POLICY_HIERARCHY_TREE,
-              currentPolicy.scope,
-              currentPolicy.verbs
+              incompletePolicy.scope,
+              incompletePolicy.verbs
             )
           );
+          setCurrentPolicy(policies);
         });
     }
     return () => {

+ 4 - 9
dashboard/src/shared/auth/AuthorizationHoc.tsx

@@ -2,15 +2,16 @@ import React, { useCallback, useContext } from "react";
 import { AuthContext } from "./AuthContext";
 import { isAuthorized } from "./authorization-helpers";
 import { ScopeType, Verbs } from "./types";
+import useAuth from "./useAuth";
 
 export const GuardedComponent = <ComponentProps extends object>(
   scope: ScopeType,
   resource: string,
   verb: Verbs | Array<Verbs>
 ) => (Component: any) => (props: ComponentProps) => {
-  const authContext = useContext(AuthContext);
+  const [isAuth] = useAuth();
 
-  if (isAuthorized(authContext.currentPolicy, scope, resource, verb)) {
+  if (isAuth(scope, resource, verb)) {
     return <Component {...props} />;
   }
 
@@ -36,13 +37,7 @@ export function withAuth<P>(
   })`;
 
   const C = (props: P) => {
-    const authContext = useContext(AuthContext);
-
-    const isAuth = useCallback(
-      (scope: ScopeType, resource: string, verb: Verbs | Array<Verbs>) =>
-        isAuthorized(authContext.currentPolicy, scope, resource, verb),
-      [authContext.currentPolicy]
-    );
+    const [isAuth] = useAuth();
     // At this point, the props being passed in are the original props the component expects.
     return <WrappedComponent {...props} isAuthorized={isAuth} />;
   };

+ 6 - 9
dashboard/src/shared/auth/RouteGuard.tsx

@@ -6,6 +6,7 @@ import { isAuthorized } from "./authorization-helpers";
 import { ScopeType, Verbs } from "./types";
 
 import Loading from "components/Loading";
+import useAuth from "./useAuth";
 
 type GuardedRouteProps = {
   scope: ScopeType;
@@ -22,15 +23,13 @@ const GuardedRoute: React.FC<RouteProps & GuardedRouteProps> = ({
   ...rest
 }) => {
   const { currentPolicy } = useContext(AuthContext);
-  const auth = useMemo(() => {
-    return isAuthorized(currentPolicy, scope, resource, verb);
-  }, [currentPolicy, scope, resource, verb]);
+  const [isAuth] = useAuth();
 
   const render = (props: any) => {
     if (!currentPolicy) {
       return <div> Loading </div>;
     }
-    if (auth) {
+    if (isAuth(scope, resource, verb)) {
       return children || <Component {...props} />;
     }
     return <UnauthorizedPage />;
@@ -40,19 +39,17 @@ const GuardedRoute: React.FC<RouteProps & GuardedRouteProps> = ({
 };
 
 export const fakeGuardedRoute = <ComponentProps extends object>(
-  scope: string,
+  scope: ScopeType,
   resource: string,
   verb: Verbs | Array<Verbs>
 ) => (Component: any) => (props: ComponentProps) => {
   const { currentPolicy } = useContext(AuthContext);
-  const auth = useMemo(() => {
-    return isAuthorized(currentPolicy, scope, resource, verb);
-  }, [currentPolicy, scope, resource, verb]);
+  const [isAuth] = useAuth();
 
   if (!currentPolicy) {
     return <Loading />;
   }
-  if (auth) {
+  if (isAuth(scope, resource, verb)) {
     return <Component {...props} />;
   }
 

+ 20 - 2
dashboard/src/shared/auth/useAuth.ts

@@ -1,7 +1,19 @@
 import { useCallback, useContext } from "react";
 import { AuthContext } from "./AuthContext";
 import { isAuthorized } from "./authorization-helpers";
-import { ScopeType, Verbs } from "./types";
+import { PolicyDocType, ScopeType, Verbs } from "./types";
+
+const isAuthorizedReducer = (
+  scope: ScopeType,
+  resource: string | string[],
+  verb: Verbs | Array<Verbs>
+) => (isAuth: boolean, currPolicy: PolicyDocType) => {
+  if (isAuth) {
+    return isAuth;
+  }
+
+  return isAuthorized(currPolicy, scope, resource, verb);
+};
 
 const useAuth = () => {
   const authContext = useContext(AuthContext);
@@ -11,7 +23,13 @@ const useAuth = () => {
       scope: ScopeType,
       resource: string | string[],
       verb: Verbs | Array<Verbs>
-    ) => isAuthorized(authContext.currentPolicy, scope, resource, verb),
+    ) =>
+      // We iterate over all the policies in search for at least one policy that will authorize
+      // the user to perform an action.
+      authContext.currentPolicy.reduce(
+        isAuthorizedReducer(scope, resource, verb),
+        false
+      ),
     [authContext.currentPolicy]
   );