Skip to content

Fix/react types errors #9

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

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
36 changes: 20 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
# React Role

[![npm](https://img.shields.io/npm/v/@permify/react-role?style=flat-square)](https://www.npmjs.com/package/@permify/react-role)
[![Twitter Follow](https://img.shields.io/twitter/follow/GetPermify?style=social)](https://twitter.com/GetPermify)
[![Discord](https://img.shields.io/discord/950799928047833088.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/MJbUjwskdH)

React Role is lightweight role based access management solution which provides components, hooks, and helper methods for controlling access checks and user permissions throughout your entire React application.

# Installation
Use npm to install:

Use npm to install:

```shell
npm install @permify/react-role
```

Use yarn to install:

```shell
Expand All @@ -37,11 +40,12 @@ const App = () => {

export default App;
```

### User Identification

In order to check user roles or permissions, you should set logged in user with ```setUser``` function. Our advice is call this method in your login functions promise.
In order to check user roles or permissions, you should set logged in user with `setUser` function. Our advice is call this method in your login functions promise.

Set the user using the ```usePermify``` hook:
Set the user using the `usePermify` hook:

```javascript

Expand All @@ -62,11 +66,11 @@ const login = async (e) => {

//
// Continue authentication flow
//
//
};
```

Or using ```PermifyContext```:
Or using `PermifyContext`:

```javascript
import React from "react";
Expand All @@ -79,13 +83,13 @@ const AuthComponent = () => {

setUser({
id: "2",
roles: ["admin", "manager"],
roles: ["admin", "manager"],
permissions: ["post-create", "user-delete", "content-show"]
})

//
// Continue authentication flow
//
//
};
};

Expand All @@ -96,16 +100,16 @@ const AuthComponent = () => {
{/* form layer */}
</form>
)}
</PermifyContext.Consumer>;
</PermifyContext.Consumer>;
)
};

export default AuthComponent;
```

## `HasAccess`
## `HasAccess`

HasAccess is a wrapper component that you can wrap around components or UI Layers that should only be accessible to users have authorization.
HasAccess is a wrapper component that you can wrap around components or UI Layers that should only be accessible to users have authorization.

You can check roles and permissions of the user with giving them as props.

Expand All @@ -119,8 +123,8 @@ const AnyComponent = () => {
..

<HasAccess
roles={["admin", "manager"]}
permissions="user-delete"
roles={["admin", "manager"]}
permissions={["user-delete"]}
renderAuthFailed={<p>You are not authorized to access!</p>}
isLoading={<Spinner/>}
>
Expand All @@ -137,7 +141,7 @@ export default App;

## `isAuthorized(roleNames, permissionNames)`

isAuthorized is a helper function that returns a Promise which resolves with true if the user is authorized for action with the given parameters, if not it resolves with false.
isAuthorized is a helper function that returns a Promise which resolves with true if the user is authorized for action with the given parameters, if not it resolves with false.

You should call it inside a conditional logic structure; for ex. if check for fetching protected information.

Expand All @@ -163,7 +167,7 @@ const AnyComponent = () => {
},[]);

return (
<>
<>
{isLoading && <span>Loading...</span>}
{dataFetched &&
//render protected component, UI Layers etc.
Expand All @@ -175,8 +179,7 @@ const AnyComponent = () => {
export default AnyComponent;
```

Stargazers
-----------
## Stargazers

[![Stargazers repo roster for @Permify/react-role](https://reporoster.com/stars/Permify/react-role)](https://github.com/Permify/react-role/stargazers)

Expand All @@ -192,4 +195,5 @@ Stargazers
</p>

# License

Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0
134 changes: 79 additions & 55 deletions src/PermifyProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,92 @@
import React, { PropsWithChildren, useCallback, useState } from "react";
import React, { ReactNode, useCallback, useState } from "react";

export interface UserPayload {
id: string,
roles: string[],
permissions: string[]
id: string;
roles: string[];
permissions: string[];
}

//context
import PermifyContext from "./PermifyContext";

const LOCAL_STORAGE_KEY_USER = "__permifyUser";

const PermifyProvider = ({
children
}: PropsWithChildren) => {
const [isLoading, setIsLoading] = useState<boolean>(false);

const updateUser = (newUser: UserPayload) => {
localStorage.setItem(LOCAL_STORAGE_KEY_USER, JSON.stringify(newUser));
};

const isAuthorized = useCallback(async (roleNames: string[], permissionNames?:string[]): Promise<boolean> => {

let hasAuthorization: boolean = false;
const storedUser = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_USER));

setIsLoading(true)
if(storedUser) {
hasAuthorization = await CheckUserHasRolesOrPermissions(storedUser, roleNames, permissionNames)
}
setIsLoading(false)

return hasAuthorization
}, []);

const CheckUserHasRolesOrPermissions = async (storedUser: UserPayload, roleNames?: string[], permissionNames?:string[]): Promise<boolean> => {
let hasRoles: boolean = false;
let hasPermissions: boolean = false;

// role checking
if(storedUser.roles && roleNames && storedUser.roles.length > 0) {
const userRoles = storedUser.roles;

const intersection = userRoles.filter(role => roleNames.includes(role));
hasRoles = intersection.length > 0
}

// permission checking
if(storedUser.permissions && permissionNames && storedUser.permissions.length > 0) {
const userPermissions = storedUser.permissions;

const intersection = userPermissions.filter(permission => permissionNames.includes(permission));
hasPermissions = intersection.length > 0
}

return hasRoles || hasPermissions
};

return <PermifyContext.Provider value={{
const PermifyProvider = ({ children }: { children: ReactNode }) => {
const [isLoading, setIsLoading] = useState<boolean>(false);

const updateUser = (newUser: UserPayload) => {
localStorage.setItem(LOCAL_STORAGE_KEY_USER, JSON.stringify(newUser));
};

const isAuthorized = useCallback(
async (
roleNames: string[],
permissionNames?: string[]
): Promise<boolean> => {
let hasAuthorization: boolean = false;
const storedUser = JSON.parse(
localStorage.getItem(LOCAL_STORAGE_KEY_USER)
);

setIsLoading(true);
if (storedUser) {
hasAuthorization = await CheckUserHasRolesOrPermissions(
storedUser,
roleNames,
permissionNames
);
}
setIsLoading(false);

return hasAuthorization;
},
[]
);

const CheckUserHasRolesOrPermissions = async (
storedUser: UserPayload,
roleNames?: string[],
permissionNames?: string[]
): Promise<boolean> => {
let hasRoles: boolean = false;
let hasPermissions: boolean = false;

// role checking
if (storedUser.roles && roleNames && storedUser.roles.length > 0) {
const userRoles = storedUser.roles;

const intersection = userRoles.filter((role) => roleNames.includes(role));
hasRoles = intersection.length > 0;
}

// permission checking
if (
storedUser.permissions &&
permissionNames &&
storedUser.permissions.length > 0
) {
const userPermissions = storedUser.permissions;

const intersection = userPermissions.filter((permission) =>
permissionNames.includes(permission)
);
hasPermissions = intersection.length > 0;
}

return hasRoles || hasPermissions;
};

return (
<PermifyContext.Provider
value={{
setUser: updateUser,
isAuthorized,
isLoading
}}>{children}
</PermifyContext.Provider>;
isLoading,
}}
>
{children}
</PermifyContext.Provider>
);
};

export default PermifyProvider;
export default PermifyProvider;
72 changes: 39 additions & 33 deletions src/components/HasAccess.tsx
Original file line number Diff line number Diff line change
@@ -1,73 +1,79 @@
import * as React from 'react';
import { useEffect, useState, useContext, PropsWithChildren } from 'react'
import * as React from "react";
import { useEffect, useState, useContext, PropsWithChildren } from "react";

export interface UserPayload {
id: string,
roles: string[],
permissions: string[]
id: string;
roles: string[];
permissions: string[];
}

const LOCAL_STORAGE_KEY_USER = "__permifyUser";

export interface HasAccessProps {
roles?: string[],
permissions?: string[]
isLoading?: React.ReactElement,
renderAuthFailed?: React.ReactElement
roles?: string[];
permissions?: string[];
isLoading?: React.ReactElement;
renderAuthFailed?: React.ReactElement;
}

const HasAccess = ({
roles,
permissions,
isLoading,
renderAuthFailed,
children
children,
}: PropsWithChildren<HasAccessProps>) => {
const [hasAccess, setHasAccess] = useState(false)
const [checking, setChecking] = useState(false)
const [hasAccess, setHasAccess] = useState(false);
const [checking, setChecking] = useState(false);

useEffect(() => {
const storedUser = JSON.parse(localStorage.getItem(LOCAL_STORAGE_KEY_USER));

if(!storedUser) {
console.log('No user provided to Permify! You should set user to perfom access check')
if (!storedUser) {
console.log(
"No user provided to Permify! You should set user to perfom access check"
);
return;
}
}

setChecking(true)
setChecking(true);

// role check
if (roles && storedUser.roles && storedUser.roles.length > 0) {
const intersection = storedUser.roles.filter((role: string) => roles.includes(role));
if (intersection.length > 0) setHasAccess(true)
const intersection = storedUser.roles.filter((role: string) =>
roles.includes(role)
);
if (intersection.length > 0) setHasAccess(true);
}

// permission check
if (permissions && storedUser.permissions && storedUser.permissions.length > 0) {
const intersection = storedUser.permissions.filter((permission: string) => permissions.includes(permission));
if (intersection.length > 0) setHasAccess(true)
if (
permissions &&
storedUser.permissions &&
storedUser.permissions.length > 0
) {
const intersection = storedUser.permissions.filter((permission: string) =>
permissions.includes(permission)
);
if (intersection.length > 0) setHasAccess(true);
}

setChecking(false)

}, [roles, permissions])
setChecking(false);
}, [roles, permissions]);

if (!hasAccess && checking) {
return isLoading
return isLoading;
}

if (hasAccess) {
return (
// children is of type ReactNode which already includes ReactFragment
{children}
)
return <>{children}</>;
}

if (renderAuthFailed) {
return renderAuthFailed
return renderAuthFailed;
}

return null
}
return null;
};

export default HasAccess;