mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-10-04 02:04:17 +00:00
Feature: Multi-user: Changes to initial admin login processing / implement JWT (#140)
* Add simple JWT parsing function * Implement user action for global store
This commit is contained in:
@@ -1,5 +1,7 @@
|
|||||||
{
|
{
|
||||||
"token": "327a2d17-ce9b-45a7-b0ff-a556536d27fb",
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX3NpZCI6IjFjNTc4MWQyLTY5MGItNDIwYy1iZDUzLTVkN2Y1NjMwMDVjOCIsInNjb3BlIjoiYWRtaW4iLCJmb3JjZV9jaGFuZ2UiOnRydWUsInBlcm1pc3Npb25zIjpbIlBST1ZJU0lPTl9VU0VSUyIsIlBST1ZJU0lPTl9TRVJWSUNFUyIsIlZJRVdfT05MWSJdLCJpYXQiOjE2NjY3OTgzMTEsImV4cCI6MTY2NjgwMTkxMX0.ZV3KnRit8WGpipfiiMAZ2AVLQ25csWje1-K6hdqxktE",
|
||||||
|
"scope": "admin",
|
||||||
"user_sid": "78131ad5-f041-4d5d-821c-47b2d8c6d015",
|
"user_sid": "78131ad5-f041-4d5d-821c-47b2d8c6d015",
|
||||||
"force_change": false
|
"force_change": false,
|
||||||
|
"permissions": ["VIEW_ONLY", "PROVISION_SERVICES", "PROVISION_USERS"]
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-2
@@ -10,6 +10,22 @@ export type IpType = "ip" | "fqdn" | "fqdn-top-level" | "invalid";
|
|||||||
|
|
||||||
export type LimitCategories = "api_rate" | "voice_call_session" | "device";
|
export type LimitCategories = "api_rate" | "voice_call_session" | "device";
|
||||||
|
|
||||||
|
/** User roles / permissions */
|
||||||
|
|
||||||
|
export type UserScopes = "admin" | "service_provider" | "account";
|
||||||
|
|
||||||
|
export type UserPermissions =
|
||||||
|
| "VIEW_ONLY"
|
||||||
|
| "PROVISION_SERVICES"
|
||||||
|
| "PROVISION_USERS";
|
||||||
|
|
||||||
|
// We'll want something like this for actual permissions implementation...
|
||||||
|
// export enum UserPermissions {
|
||||||
|
// VIEW_ONLY = 0,
|
||||||
|
// PROVISION_SERVICES = 1,
|
||||||
|
// PROVISION_USERS = 2,
|
||||||
|
// }
|
||||||
|
|
||||||
/** Status codes */
|
/** Status codes */
|
||||||
|
|
||||||
export enum StatusCodes {
|
export enum StatusCodes {
|
||||||
@@ -90,12 +106,13 @@ export interface PasswordSettings {
|
|||||||
/** API responses/payloads */
|
/** API responses/payloads */
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
|
scope: UserScopes;
|
||||||
user_sid: string;
|
user_sid: string;
|
||||||
|
permissions: UserPermissions[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserLogin {
|
export interface UserLogin extends User {
|
||||||
token: string;
|
token: string;
|
||||||
user_sid: string;
|
|
||||||
force_change: boolean;
|
force_change: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ export const Navi = ({
|
|||||||
|
|
||||||
/** Fetch service providers */
|
/** Fetch service providers */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
dispatch({ type: "user" });
|
||||||
dispatch({ type: "serviceProviders" });
|
dispatch({ type: "serviceProviders" });
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -166,12 +167,12 @@ export const Navi = ({
|
|||||||
<Icons.PlusCircle />
|
<Icons.PlusCircle />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* Until we have the APIs this initial UI is not accessible */}
|
{/* Intentionally hiding this as we will need to work this out as we go... */}
|
||||||
{user && (
|
{/* ACL component will need to be updated for new user scope/permissions handling */}
|
||||||
|
{false && user && (
|
||||||
<div className="navi__user">
|
<div className="navi__user">
|
||||||
{/* Seed should be user sid when we have the APIs for it... */}
|
|
||||||
<Blockies
|
<Blockies
|
||||||
seed="jambonz"
|
seed={user!.user_sid}
|
||||||
size={6}
|
size={6}
|
||||||
scale={6}
|
scale={6}
|
||||||
color={getCssVar("--jambonz")}
|
color={getCssVar("--jambonz")}
|
||||||
|
|||||||
@@ -60,6 +60,26 @@ export const setToken = (token: string) => {
|
|||||||
localStorage.setItem(storageKey, token);
|
localStorage.setItem(storageKey, token);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode data from a JWT
|
||||||
|
* https://stackoverflow.com/questions/38552003/how-to-decode-jwt-token-in-javascript-without-using-a-library
|
||||||
|
*/
|
||||||
|
export const parseJwt = (token: string) => {
|
||||||
|
const base64Url = token.split(".")[1];
|
||||||
|
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
||||||
|
const jsonPayload = decodeURIComponent(
|
||||||
|
window
|
||||||
|
.atob(base64)
|
||||||
|
.split("")
|
||||||
|
.map((c) => {
|
||||||
|
return "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2);
|
||||||
|
})
|
||||||
|
.join("")
|
||||||
|
);
|
||||||
|
|
||||||
|
return JSON.parse(jsonPayload);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provider hook that creates auth object and handles state
|
* Provider hook that creates auth object and handles state
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { getUser, getServiceProviders } from "src/api";
|
import { getServiceProviders } from "src/api";
|
||||||
import { sortLocaleName } from "src/utils";
|
import { sortLocaleName } from "src/utils";
|
||||||
|
import { getToken, parseJwt } from "src/router/auth";
|
||||||
|
|
||||||
import type { State, Action } from "./types";
|
import type { State, Action } from "./types";
|
||||||
import type { ServiceProvider, User } from "src/api/types";
|
import type { ServiceProvider, User } from "src/api/types";
|
||||||
@@ -61,8 +62,9 @@ export const currentServiceProviderAction = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const userAsyncAction = async (): Promise<User> => {
|
export const userAsyncAction = async (): Promise<User> => {
|
||||||
const response = await getUser("user_sid");
|
const token = getToken();
|
||||||
return response.json;
|
const { user_sid, permissions, scope } = parseJwt(token);
|
||||||
|
return { user_sid, permissions, scope };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const serviceProvidersAsyncAction = async (): Promise<
|
export const serviceProvidersAsyncAction = async (): Promise<
|
||||||
|
|||||||
+2
-1
@@ -6,6 +6,7 @@ import { AuthContext } from "src/router/auth";
|
|||||||
import { MSG_SOMETHING_WRONG } from "src/constants";
|
import { MSG_SOMETHING_WRONG } from "src/constants";
|
||||||
|
|
||||||
import type { AuthStateContext } from "src/router/auth";
|
import type { AuthStateContext } from "src/router/auth";
|
||||||
|
import type { UserLogin } from "src/api/types";
|
||||||
|
|
||||||
import userLogin from "../../cypress/fixtures/userLogin.json";
|
import userLogin from "../../cypress/fixtures/userLogin.json";
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ type LayoutProviderProps = TestProviderProps & {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const signinError = () => Promise.reject(MSG_SOMETHING_WRONG);
|
export const signinError = () => Promise.reject(MSG_SOMETHING_WRONG);
|
||||||
export const signinSuccess = () => Promise.resolve(userLogin);
|
export const signinSuccess = () => Promise.resolve(userLogin as UserLogin);
|
||||||
export const signout = () => undefined;
|
export const signout = () => undefined;
|
||||||
export const authProps: AuthStateContext = {
|
export const authProps: AuthStateContext = {
|
||||||
token: "",
|
token: "",
|
||||||
|
|||||||
Reference in New Issue
Block a user