From a47d3f142f45c86d1808a93312812881afd83e7e Mon Sep 17 00:00:00 2001 From: Jatin Mehta Date: Thu, 17 Oct 2024 18:51:17 +0530 Subject: [PATCH 1/4] feat: list agama communities project --- admin-ui/app/locales/en/translation.json | 10 +- .../components/Agama/AgamaListPage.js | 366 ++++++++++++++---- 2 files changed, 298 insertions(+), 78 deletions(-) diff --git a/admin-ui/app/locales/en/translation.json b/admin-ui/app/locales/en/translation.json index 477aa55d5..c3f72c68b 100644 --- a/admin-ui/app/locales/en/translation.json +++ b/admin-ui/app/locales/en/translation.json @@ -23,6 +23,7 @@ "currentMonth": "Current Month", "currentYear": "Current Year", "choose": "Choose", + "deploy":"Deploy", "edit": "Edit", "next": "Next", "no": "No", @@ -623,7 +624,9 @@ "source_backend_ldap_servers": "Source Backend LDAP Servers", "inum_db_server": "Inum DB Server", "static_configuration": "Static Configuration", - "dynamic_configuration": "Dynamic Configuration" + "dynamic_configuration": "Dynamic Configuration", + "upload_agama_project":"Upload Agama project", + "add_community_project":"Add a Community Project" }, "messages": { "add_permission": "Add Permission", @@ -713,7 +716,8 @@ "drag_sha_file": "Drag .sha256sum file here, or click to select file", "see_project_details": "See project details", "add_ssa": "Add SSA", - "copied": "Copied" + "copied": "Copied", + "no_data_found": "No data found" }, "tooltips": { "add_attribute": "Add attribute", @@ -840,6 +844,8 @@ "scopes": "Scopes", "scripts": "Custom Scripts", "schemaName": "Schema Name", + "select_project_deploy":"Select a project to deploy", + "add_new_agama_project":"Add a new Agama Project", "user_management": "User Management", "fido_management": "FIDO Configuration", "saml_management": "SAML", diff --git a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js index 32f206f1e..85d328aac 100644 --- a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js +++ b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js @@ -24,9 +24,15 @@ import CircularProgress from "@mui/material/CircularProgress"; import InfoIcon from "@mui/icons-material/Info"; import AgamaProjectConfigModal from "./AgamaProjectConfigModal"; import { updateToast } from "Redux/features/toastSlice"; -import { isEmpty } from "lodash"; +import { isEmpty, set } from "lodash"; import { getJsonConfig } from "Plugins/auth-server/redux/features/jsonConfigSlice"; import SettingsIcon from "@mui/icons-material/Settings"; +import Checkbox from "@mui/material/Checkbox"; +import FormGroup from "@mui/material/FormGroup"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import FormLabel from "@mui/material/FormLabel"; +import GluuTabs from "Routes/Apps/Gluu/GluuTabs"; +import axios from "axios"; const dateTimeFormatOptions = { year: "2-digit", @@ -56,6 +62,8 @@ function AgamaListPage() { const [shaFileName, setShaFileName] = useState(""); const [listData, setListData] = useState([]); const [selectedRow, setSelectedRow] = useState({}); + const [repoName, setRepoName] = useState(null); + const [repositoriesData, setRespositoriesData] = useState([]); const configuration = useSelector( (state) => state.jsonConfigReducer.configuration ); @@ -68,6 +76,7 @@ function AgamaListPage() { const selectedTheme = theme.state.theme; const themeColors = getThemeColor(selectedTheme); const bgThemeColor = { background: themeColors.background }; + function convertFileToByteArray(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -82,7 +91,21 @@ function AgamaListPage() { }); } + function fetchRespositoryData() { + axios + .get("https://github.com/orgs/GluuFederation/repositories?q=agama-", { + headers: { + Accept: "application/json", + }, + }) + .then((response) => { + setRespositoriesData(response.data.payload.repositories); + }) + .catch((error) => console.log(error)); + } + useEffect(() => { + fetchRespositoryData(); if (isEmpty(configuration)) { dispatch(getJsonConfig({ action: {} })); } @@ -95,7 +118,7 @@ function AgamaListPage() { file: file, }; - dispatch(addAgama(object)); + //dispatch(addAgama(object)); setProjectName(""); setShowAddModal(false); @@ -322,6 +345,258 @@ function AgamaListPage() { } }; + //Modal Tabs + const tabNames = [ + { name: t("menus.upload_agama_project"), path: "" }, + { name: t("menus.add_community_project"), path: "" }, + ]; + + function convertUrlToByteArray(repoUrl) { + console.log("Converting URL to byte array:", repoUrl); + + return new Promise(async (resolve, reject) => { + await axios({ + url: repoUrl, + method: 'GET', + headers: { + 'Accept': 'application/gama' // Add the appropriate header for accepting a gama file + }, + responseType: 'blob' // Ensures the response is treated as a Blob + }) + .then((response) => { + const blob = response.data; // Get the blob from response data + const reader = new FileReader(); + reader.readAsArrayBuffer(blob); + reader.onload = () => { + const byteArray = new Uint8Array(reader.result); + resolve(byteArray); + }; + reader.onerror = (error) => { + reject(error); + }; + }) + .catch((error) => { + console.error("Error converting URL to byte array:", error); + reject(error); + }); + }); + } + + const handleDeploy = async () => { + console.log("Deploying project", repoName); + let repoUrl = null; + let file = null; + + await axios(`https://api.github.com/repos/GluuFederation/${repoName}/releases/latest`) + .then((response) => { + for (const asset of response.data.assets) { + if (asset.name === `${repoName}.gama`) { + repoUrl = asset.browser_download_url; + return; + } + } + }) + .catch((error) => console.log(error)); + + try { + console.log("RepoUrl", repoUrl); + file = await convertUrlToByteArray(repoUrl); + //console.log("File", file); + + if (repoName && file) { + const object = { + name: repoName, + file: file, + }; + + //dispatch(addAgama(object)); + } + } catch (error) { + console.log(error); + } + + setShowAddModal(false); + setRepoName(null); + }; + + const tabToShow = (tabName) => { + switch (tabName) { + case t("menus.upload_agama_project"): + return ( + <> + +
+ + {selectedFileName ? ( + Selected File : {selectedFileName} + ) : ( +

{t("messages.drag_agama_file")}

+ )} +
+
+
+ + {shaFile ? ( + Selected File : {shaFileName} + ) : ( +

{t("messages.drag_sha_file")}

+ )} +
+
+
+ {shaFile && + selectedFileName && + !shaStatus && + "SHA256 not verified"} +
+
+ {shaFile && selectedFileName && shaStatus && "SHA256 verified"} +
+ {getProjectName && ( + setProjectName(e.target.value)} + /> + )} +
+ + +   + + + + ); + case t("menus.add_community_project"): + return ( + <> + + + + {t("titles.select_project_deploy")} + + +
+ {repositoriesData?.length ? ( + repositoriesData.map((item, index) => ( + + setRepoName( + repoName === item.name ? null : item.name + ) + } + sx={{ + transform: "scale(1.5)", + paddingTop: "6px", + }} + /> + } + label={ +
+
{item.name}
+
+ {item.description} +
+
+ } + sx={{ + alignItems: "flex-start", + marginBottom: "16px", + }} + /> + )) + ) : ( +
+ {t("messages.no_data_found")} +
+ )} +
+
+
+ + +   + + + + ); + } + }; + return ( <> {showConfigModal && ( @@ -419,80 +694,19 @@ function AgamaListPage() { }} /> - - {t("titles.add_agama_project")} - -
- - {selectedFileName ? ( - Selected File : {selectedFileName} - ) : ( -

{t("messages.drag_agama_file")}

- )} -
-
-
- - {shaFile ? ( - Selected File : {shaFileName} - ) : ( -

{t("messages.drag_sha_file")}

- )} -
-
-
- {shaFile && - selectedFileName && - !shaStatus && - "SHA256 not verified"} -
-
- {shaFile && selectedFileName && shaStatus && "SHA256 verified"} -
- {getProjectName && ( - setProjectName(e.target.value)} - /> - )} -
- - -   - - + + {t("titles.add_new_agama_project")} + + + From 8f15859111f0b8adf71dcb01aeaabd47475a829f Mon Sep 17 00:00:00 2001 From: Jatin Mehta Date: Mon, 21 Oct 2024 10:52:30 +0530 Subject: [PATCH 2/4] feat(admin-ui): allow users to install Agama Lab community projects from Admin UI --- admin-ui/app/locales/fr/translation.json | 10 +- admin-ui/app/locales/pt/translation.json | 10 +- .../components/Agama/AgamaListPage.js | 137 ++++++++++-------- 3 files changed, 92 insertions(+), 65 deletions(-) diff --git a/admin-ui/app/locales/fr/translation.json b/admin-ui/app/locales/fr/translation.json index a4d824dbf..4574dfddb 100644 --- a/admin-ui/app/locales/fr/translation.json +++ b/admin-ui/app/locales/fr/translation.json @@ -99,7 +99,9 @@ "source_backend_ldap_servers": "Serveurs LDAP dorsaux sources", "inum_db_server": "Serveur de base de données Inum", "static_configuration": "Configuration statique", - "dynamic_configuration": "Configuration dynamique" + "dynamic_configuration": "Configuration dynamique", + "upload_agama_project": "Télécharger le projet Agama", + "add_community_project": "Ajouter un projet communautaire" }, "actions": { "accept": "J'accepte", @@ -120,6 +122,7 @@ "change_bind_password": "Modifier le mot de passe de liaison", "set_password": "Définir le mot de passe", "choose": "Choisir", + "deploy": "Déployer", "edit": "Éditer", "next": "Prochain", "no": "Non", @@ -650,7 +653,8 @@ "see_project_details": "Voir les détails du projet", "add_ssa": "Ajouter SSA", "copied": "Copié", - "edit_acr": "Modifier ACR" + "edit_acr": "Modifier ACR", + "no_data_found": "Aucune donnée trouvée" }, "tooltips": { "add_attribute": "Ajouter un attribut", @@ -760,6 +764,8 @@ "scopes": "Portées", "scripts": "Scénarios", "schemaName": "Nom du schéma", + "select_project_deploy": "Sélectionnez un projet à déployer", + "add_new_agama_project": "Ajouter un nouveau projet Agama", "user_management": "Gestion des utilisateurs", "ssa_management": "SSA Gestion", "jans_ink": "Jans Link", diff --git a/admin-ui/app/locales/pt/translation.json b/admin-ui/app/locales/pt/translation.json index 79414469d..ed366df62 100644 --- a/admin-ui/app/locales/pt/translation.json +++ b/admin-ui/app/locales/pt/translation.json @@ -97,7 +97,9 @@ "source_backend_ldap_servers": "Servidores LDAP de back-end de origem", "inum_db_server": "Servidor DB Inum", "static_configuration": "Configuração estática", - "dynamic_configuration": "Configuração Dinâmica" + "dynamic_configuration": "Configuração Dinâmica", + "upload_agama_project": "Fazer upload do projeto Agama", + "add_community_project": "Adicionar um projeto comunitário" }, "actions": { "accept": "Aceitar", @@ -118,6 +120,7 @@ "set_password": "Configurar senha", "cancel": "Cancelar", "choose": "Escolher", + "deploy": "Implantar", "edit": "Editar", "next": "Próxima", "no": "Não", @@ -645,7 +648,8 @@ "see_project_details": "Ver detalhes do projeto", "add_ssa": "Adicionar SSA", "copied": "Copiado", - "edit_acr": "Editar ACR" + "edit_acr": "Editar ACR", + "no_data_found": "Nenhum dado encontrado" }, "tooltips": { "add_attribute": "Adicionar atributo", @@ -755,6 +759,8 @@ "scopes": "Scopes", "scripts": "Scripts", "schemaName": "Nome do esquema", + "select_project_deploy": "Selecione um projeto para implantar", + "add_new_agama_project": "Adicionar um novo projeto Agama", "user_management": "Gerenciamento de usuários", "project_configuration": "Configuração do projeto", "ssa_management": "SSA Gerenciamento", diff --git a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js index 85d328aac..3854374ab 100644 --- a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js +++ b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js @@ -32,6 +32,7 @@ import FormGroup from "@mui/material/FormGroup"; import FormControlLabel from "@mui/material/FormControlLabel"; import FormLabel from "@mui/material/FormLabel"; import GluuTabs from "Routes/Apps/Gluu/GluuTabs"; +import { toast } from "react-toastify"; import axios from "axios"; const dateTimeFormatOptions = { @@ -63,7 +64,10 @@ function AgamaListPage() { const [listData, setListData] = useState([]); const [selectedRow, setSelectedRow] = useState({}); const [repoName, setRepoName] = useState(null); - const [repositoriesData, setRespositoriesData] = useState([]); + const [repositoriesData, setRespositoriesData] = useState({ + loading: true, + repositories: [], + }); const configuration = useSelector( (state) => state.jsonConfigReducer.configuration ); @@ -91,21 +95,40 @@ function AgamaListPage() { }); } - function fetchRespositoryData() { - axios - .get("https://github.com/orgs/GluuFederation/repositories?q=agama-", { - headers: { - Accept: "application/json", - }, - }) - .then((response) => { - setRespositoriesData(response.data.payload.repositories); - }) - .catch((error) => console.log(error)); + async function fetchRespositoryData() { + try { + const response = await axios.get( + "https://github.com/orgs/GluuFederation/repositories?q=agama-", + { + headers: { + Accept: "application/json", + }, + } + ); + if ( + response.data && + response.data.payload && + response.data.payload.repositories + ) { + const filteredRepositoriesData = + response.data.payload.repositories.filter( + (repo) => + !listData.find( + (project) => + project.details.projectMetadata.projectName === repo.name + ) + ); + setRespositoriesData({ + loading: false, + repositories: filteredRepositoriesData, + }); + } + } catch (error) { + setRespositoriesData({ loading: false, repositories: [] }); + } } useEffect(() => { - fetchRespositoryData(); if (isEmpty(configuration)) { dispatch(getJsonConfig({ action: {} })); } @@ -117,8 +140,7 @@ function AgamaListPage() { name: projectName, file: file, }; - - //dispatch(addAgama(object)); + dispatch(addAgama(object)); setProjectName(""); setShowAddModal(false); @@ -247,6 +269,7 @@ function AgamaListPage() { setSelectedFileName(null); setGetProjectName(false); setSHAfile(null); + fetchRespositoryData(); if (isAgamaEnabled) { setShowAddModal(true); } else { @@ -351,72 +374,56 @@ function AgamaListPage() { { name: t("menus.add_community_project"), path: "" }, ]; - function convertUrlToByteArray(repoUrl) { - console.log("Converting URL to byte array:", repoUrl); - + async function convertUrlToByteArray(repoUrl) { return new Promise(async (resolve, reject) => { - await axios({ - url: repoUrl, - method: 'GET', - headers: { - 'Accept': 'application/gama' // Add the appropriate header for accepting a gama file - }, - responseType: 'blob' // Ensures the response is treated as a Blob - }) - .then((response) => { - const blob = response.data; // Get the blob from response data - const reader = new FileReader(); - reader.readAsArrayBuffer(blob); - reader.onload = () => { - const byteArray = new Uint8Array(reader.result); - resolve(byteArray); - }; - reader.onerror = (error) => { - reject(error); - }; - }) - .catch((error) => { - console.error("Error converting URL to byte array:", error); - reject(error); + try { + const response = await axios.get(repoUrl, { + responseType: "arraybuffer", }); + if (response && response.data) { + const byteArray = new Uint8Array(response.data); + resolve(byteArray); + } + } catch (error) { + console.error("Error converting URL to byte array:", error); + reject(error); + } }); } const handleDeploy = async () => { console.log("Deploying project", repoName); + let repoUrl = null; let file = null; - - await axios(`https://api.github.com/repos/GluuFederation/${repoName}/releases/latest`) - .then((response) => { + try { + const response = await axios( + `https://api.github.com/repos/GluuFederation/${repoName}/releases/latest` + ); for (const asset of response.data.assets) { - if (asset.name === `${repoName}.gama`) { + if (asset.name.endsWith(".gama")) { repoUrl = asset.browser_download_url; - return; + break; } } - }) - .catch((error) => console.log(error)); - - try { - console.log("RepoUrl", repoUrl); + if (!repoUrl) { + toast.error("File not found"); + return; + } file = await convertUrlToByteArray(repoUrl); - //console.log("File", file); - if (repoName && file) { const object = { name: repoName, file: file, }; - - //dispatch(addAgama(object)); + dispatch(addAgama(object)); } } catch (error) { - console.log(error); + toast.error("File not found"); + } finally { + setShowAddModal(false); + setRepoName(null); } - - setShowAddModal(false); - setRepoName(null); }; const tabToShow = (tabName) => { @@ -524,10 +531,13 @@ function AgamaListPage() { maxHeight: "500px", overflowY: "auto", overflowX: "hidden", + alignItems: `${repositoriesData.loading && "center"}`, }} > - {repositoriesData?.length ? ( - repositoriesData.map((item, index) => ( + {repositoriesData.loading ? ( + + ) : repositoriesData.repositories?.length ? ( + repositoriesData.repositories.map((item, index) => ( handleDeploy()} > + {loading || isConfigLoading ? ( + <> +   + + ) : null} {t("actions.deploy")}   From c4169e5704b80b166ded9b4637aba9602a481245 Mon Sep 17 00:00:00 2001 From: Jatin Mehta Date: Fri, 20 Dec 2024 15:53:17 +0530 Subject: [PATCH 3/4] agama download api --- .../components/Agama/AgamaListPage.js | 78 +++------- .../auth-server/components/AuthN/index.js | 5 +- .../plugins/auth-server/redux/api/AgamaApi.js | 53 +++++-- .../auth-server/redux/features/agamaSlice.js | 31 +++- .../auth-server/redux/sagas/AgamaSaga.js | 139 ++++++++++++------ 5 files changed, 192 insertions(+), 114 deletions(-) diff --git a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js index 3854374ab..a9892acb9 100644 --- a/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js +++ b/admin-ui/plugins/auth-server/components/Agama/AgamaListPage.js @@ -1,6 +1,6 @@ import React, { useState, useEffect, useContext, useCallback } from "react"; import { useSelector, useDispatch } from "react-redux"; -import { Card, CardBody, Input } from "Components"; +import { Card, Input } from "Components"; import applicationStyle from "Routes/Apps/Gluu/styles/applicationstyle"; import { useTranslation } from "react-i18next"; import SetTitle from "Utils/SetTitle"; @@ -34,6 +34,10 @@ import FormLabel from "@mui/material/FormLabel"; import GluuTabs from "Routes/Apps/Gluu/GluuTabs"; import { toast } from "react-toastify"; import axios from "axios"; +import { + getAgamaRepository, + getAgamaRepositoryFile, +} from "../../redux/features/agamaSlice"; const dateTimeFormatOptions = { year: "2-digit", @@ -97,32 +101,7 @@ function AgamaListPage() { async function fetchRespositoryData() { try { - const response = await axios.get( - "https://github.com/orgs/GluuFederation/repositories?q=agama-", - { - headers: { - Accept: "application/json", - }, - } - ); - if ( - response.data && - response.data.payload && - response.data.payload.repositories - ) { - const filteredRepositoriesData = - response.data.payload.repositories.filter( - (repo) => - !listData.find( - (project) => - project.details.projectMetadata.projectName === repo.name - ) - ); - setRespositoriesData({ - loading: false, - repositories: filteredRepositoriesData, - }); - } + dispatch(getAgamaRepository()); } catch (error) { setRespositoriesData({ loading: false, repositories: [] }); } @@ -199,7 +178,10 @@ function AgamaListPage() { }, }); - const { totalItems, loading } = useSelector((state) => state.agamaReducer); + const { totalItems, loading, agamaRepostoriesList } = useSelector( + (state) => state.agamaReducer + ); + const agamaList = useSelector((state) => state.agamaReducer.agamaList); const permissions = useSelector((state) => state.authReducer.permissions); SetTitle(t("titles.agama")); @@ -392,32 +374,13 @@ function AgamaListPage() { } const handleDeploy = async () => { - console.log("Deploying project", repoName); - - let repoUrl = null; let file = null; try { - const response = await axios( - `https://api.github.com/repos/GluuFederation/${repoName}/releases/latest` + const repo = agamaRepostoriesList.projects.find( + (item) => item["repository-name"] === repoName ); - for (const asset of response.data.assets) { - if (asset.name.endsWith(".gama")) { - repoUrl = asset.browser_download_url; - break; - } - } - if (!repoUrl) { - toast.error("File not found"); - return; - } - file = await convertUrlToByteArray(repoUrl); - if (repoName && file) { - const object = { - name: repoName, - file: file, - }; - dispatch(addAgama(object)); - } + dispatch(getAgamaRepositoryFile()); + // dispatch(addAgama(object)); } catch (error) { toast.error("File not found"); } finally { @@ -531,21 +494,22 @@ function AgamaListPage() { maxHeight: "500px", overflowY: "auto", overflowX: "hidden", - alignItems: `${repositoriesData.loading && "center"}`, }} > - {repositoriesData.loading ? ( + {loading ? ( - ) : repositoriesData.repositories?.length ? ( - repositoriesData.repositories.map((item, index) => ( + ) : agamaRepostoriesList?.projects.length ? ( + agamaRepostoriesList.projects.map((item, index) => ( setRepoName( - repoName === item.name ? null : item.name + repoName === item["repository-name"] + ? null + : item["repository-name"] ) } sx={{ diff --git a/admin-ui/plugins/auth-server/components/AuthN/index.js b/admin-ui/plugins/auth-server/components/AuthN/index.js index d82437e8b..655e805c9 100644 --- a/admin-ui/plugins/auth-server/components/AuthN/index.js +++ b/admin-ui/plugins/auth-server/components/AuthN/index.js @@ -1,9 +1,8 @@ -import React, { useState, useEffect, useContext } from "react"; +import React from "react"; import AuthNListPage from "./AuthNListPage"; import { useTranslation } from "react-i18next"; import GluuTabs from "Routes/Apps/Gluu/GluuTabs"; -import GluuLoader from "Routes/Apps/Gluu/GluuLoader"; -import { Card, CardBody } from "Components"; +import { Card } from "Components"; import applicationStyle from "Routes/Apps/Gluu/styles/applicationstyle"; import { useSelector } from "react-redux"; diff --git a/admin-ui/plugins/auth-server/redux/api/AgamaApi.js b/admin-ui/plugins/auth-server/redux/api/AgamaApi.js index 91c897a6d..f2bbf7581 100644 --- a/admin-ui/plugins/auth-server/redux/api/AgamaApi.js +++ b/admin-ui/plugins/auth-server/redux/api/AgamaApi.js @@ -14,15 +14,15 @@ export default class AgamaApi { }) } - addAgama = ({payload}) => { - const {file, name, token} = payload + addAgama = ({ payload }) => { + const { file, name, token } = payload return new Promise((resolve, reject) => { - axios.post('/api/v1/agama-deployment/' + name, file, { - headers: { - Authorization: 'Bearer ' + token, - 'Content-Type': 'application/zip', - } - }) + axios.post('/api/v1/agama-deployment/' + name, file, { + headers: { + Authorization: 'Bearer ' + token, + 'Content-Type': 'application/zip', + } + }) .then((response) => { resolve(response) }) @@ -31,11 +31,46 @@ export default class AgamaApi { }) }) } - deleteAgama = ({payload}) => { + deleteAgama = ({ payload }) => { return new Promise((resolve, reject) => { this.api.deleteAgamaPrj(payload.name, (error, data) => { handleResponse(error, reject, resolve, data) }) }) } + + getAgamaRepositories = (payload) => { + const { token } = payload + return new Promise((resolve, reject) => { + axios.get('/api/v1/agama-repo/', { + headers: { + Authorization: 'Bearer ' + token + } + }) + .then((response) => { + resolve(response) + }) + .catch((error) => { + reject(error) + }) + }) + } + + + getAgamaRepositoryFile = (payload) => { + const { downloadurl } = payload + return new Promise((resolve, reject) => { + axios.get(`/api/v1/agama-repo/download/?downloadLink=${decodeURIComponent(downloadurl)}`, { + headers: { + Authorization: 'Bearer ' + token + } + }) + .then((response) => { + resolve(response) + }) + .catch((error) => { + reject(error) + }) + }) + } } diff --git a/admin-ui/plugins/auth-server/redux/features/agamaSlice.js b/admin-ui/plugins/auth-server/redux/features/agamaSlice.js index 82ae3ee70..b2e2bf986 100644 --- a/admin-ui/plugins/auth-server/redux/features/agamaSlice.js +++ b/admin-ui/plugins/auth-server/redux/features/agamaSlice.js @@ -5,7 +5,9 @@ const initialState = { agamaList: [], loading: false, totalItems: 0, - entriesCount: 0 + entriesCount: 0, + agamaRepostoriesList:[], + agamaFileResponse:null } const agamaSlice = createSlice({ @@ -31,7 +33,28 @@ const agamaSlice = createSlice({ }, getAddAgamaResponse: (state) => { state.loading = false + }, + getAgamaRepository: (state) => { + state.loading = true + }, + getAgamaRepositoryFile: (state) => { + console.log('action.payload', action) + state.loading = true + }, + getAgamaRepositoriesResponse: (state, action) => { + + state.loading = false + if (action.payload) { + state.agamaRepostoriesList = action.payload || [] + } + }, + getAgamaRepositoryFileResponse: (state, action) => { + state.loading = false + if (action.payload) { + state.agamaFileResponse = action.payload || [] + } } + } }) @@ -40,7 +63,11 @@ export const { getAgamaResponse, deleteAgama, addAgama, - getAddAgamaResponse + getAddAgamaResponse, + getAgamaRepository, + getAgamaRepositoriesResponse, + getAgamaRepositoryFile, + getAgamaRepositoryFileResponse } = agamaSlice.actions export { initialState } export const { actions, reducer, state } = agamaSlice diff --git a/admin-ui/plugins/auth-server/redux/sagas/AgamaSaga.js b/admin-ui/plugins/auth-server/redux/sagas/AgamaSaga.js index 2a876e8af..13b12916a 100644 --- a/admin-ui/plugins/auth-server/redux/sagas/AgamaSaga.js +++ b/admin-ui/plugins/auth-server/redux/sagas/AgamaSaga.js @@ -1,79 +1,132 @@ -import { call, all, put, fork, takeLatest, select } from 'redux-saga/effects' -import { isFourZeroOneError } from 'Utils/TokenController' -import { getAPIAccessToken } from 'Redux/features/authSlice' -import AgamaApi from '../api/AgamaApi' -import { getClient } from 'Redux/api/base' -import { getAgamaResponse, getAddAgamaResponse, getAgama } from '../features/agamaSlice' -const JansConfigApi = require('jans_config_api') +import { call, all, put, fork, takeLatest, select } from "redux-saga/effects"; +import { isFourZeroOneError } from "Utils/TokenController"; +import { getAPIAccessToken } from "Redux/features/authSlice"; +import AgamaApi from "../api/AgamaApi"; +import { getClient } from "Redux/api/base"; +import { + getAgamaResponse, + getAddAgamaResponse, + getAgama, + getAgamaRepositoriesResponse, +} from "../features/agamaSlice"; +const JansConfigApi = require("jans_config_api"); function* newFunction() { - const token = yield select((state) => state.authReducer.token.access_token) - const issuer = yield select((state) => state.authReducer.issuer) + const token = yield select((state) => state.authReducer.token.access_token); + const issuer = yield select((state) => state.authReducer.issuer); const api = new JansConfigApi.AgamaApi( - getClient(JansConfigApi, token, issuer), - ) - return new AgamaApi(api) + getClient(JansConfigApi, token, issuer) + ); + return new AgamaApi(api); } export function* getAgamas() { try { - const api = yield* newFunction() - const data = yield call(api.getAgama, {}) - yield put(getAgamaResponse(data)) - return data + const api = yield* newFunction(); + const data = yield call(api.getAgama, {}); + yield put(getAgamaResponse(data)); + return data; } catch (e) { if (isFourZeroOneError(e)) { - const jwt = yield select((state) => state.authReducer.userinfo_jwt) - yield put(getAPIAccessToken(jwt)) + const jwt = yield select((state) => state.authReducer.userinfo_jwt); + yield put(getAPIAccessToken(jwt)); } - return e + return e; } } -export function* addAgama(payload){ - const token = yield select((state) => state.authReducer.token.access_token) - try{ - payload.payload['token'] = token - const api = yield* newFunction() - const data = yield call(api.addAgama, payload) - yield put(getAddAgamaResponse(data)) - yield put(getAgama()) - return data - }catch(e){ - yield put(getAddAgamaResponse(null)) - return e +export function* addAgama(payload) { + const token = yield select((state) => state.authReducer.token.access_token); + try { + payload.payload["token"] = token; + const api = yield* newFunction(); + const data = yield call(api.addAgama, payload); + yield put(getAddAgamaResponse(data)); + yield put(getAgama()); + return data; + } catch (e) { + yield put(getAddAgamaResponse(null)); + return e; } } export function* deleteAgamas(payload) { try { - const api = yield* newFunction() - const data = yield call(api.deleteAgama, payload) - yield put(getAgama()) - return data + const api = yield* newFunction(); + const data = yield call(api.deleteAgama, payload); + yield put(getAgama()); + return data; + } catch (e) { + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt); + yield put(getAPIAccessToken(jwt)); + } + return e; + } +} + +export function* getAgamaRepository() { + try { + const token = yield select((state) => state.authReducer.token.access_token); + + const payload = { token }; + const api = yield* newFunction(); + const data = yield call(api.getAgamaRepositories, payload); + + yield put(getAgamaRepositoriesResponse(data.data)); + return data; } catch (e) { if (isFourZeroOneError(e)) { - const jwt = yield select((state) => state.authReducer.userinfo_jwt) - yield put(getAPIAccessToken(jwt)) + const jwt = yield select((state) => state.authReducer.userinfo_jwt); + yield put(getAPIAccessToken(jwt)); } - return e + return e; } } +export function* getAgamaRepositoryFile(payload) { + try { + const token = yield select((state) => state.authReducer.token.access_token); + console.log(payload) + const payload = { token }; + const api = yield* newFunction(); + const data = yield call(api.getAgamaRepositoryFile, payload); + yield put(getAgamaRepositoriesResponse(data.data)); + return data; + } catch (e) { + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt); + yield put(getAPIAccessToken(jwt)); + } + return e; + } +} export function* watchGetAgama() { - yield takeLatest('agama/getAgama', getAgamas) + yield takeLatest("agama/getAgama", getAgamas); } export function* watchDeleteAgama() { - yield takeLatest('agama/deleteAgama', deleteAgamas) + yield takeLatest("agama/deleteAgama", deleteAgamas); } export function* watchAddAgama() { - yield takeLatest('agama/addAgama', addAgama) + yield takeLatest("agama/addAgama", addAgama); +} +export function* watchGetAgamaRepository() { + yield takeLatest("agama/getAgamaRepository", getAgamaRepository); } +export function* watchAgamaRepositoryFile() { + yield takeLatest("agama/getAgamaRepositoryFile", getAgamaRepositoryFile); +} export default function* rootSaga() { - yield all([fork(watchGetAgama), fork(watchDeleteAgama), fork(watchAddAgama)]) -} \ No newline at end of file + yield all([ + fork(watchGetAgama), + fork(watchDeleteAgama), + fork(watchAddAgama), + fork(watchGetAgamaRepository), + fork(watchAgamaRepositoryFile), + ]); +} From ea95c89993fa08949afa1f980a9c2eeced558d00 Mon Sep 17 00:00:00 2001 From: Jatin Mehta Date: Sun, 5 Jan 2025 23:59:18 +0530 Subject: [PATCH 4/4] fix(admin-ui): update token script should reject the tampered user-info-jwt --- admin-ui/app/locales/en/translation.json | 5 +- admin-ui/app/locales/fr/translation.json | 5 +- admin-ui/app/locales/pt/translation.json | 5 +- .../routes/Apps/Gluu/GluuPermissionModal.js | 49 +++++++++++++++++++ .../app/routes/Dashboards/DashboardPage.js | 30 +++++++----- 5 files changed, 79 insertions(+), 15 deletions(-) create mode 100644 admin-ui/app/routes/Apps/Gluu/GluuPermissionModal.js diff --git a/admin-ui/app/locales/en/translation.json b/admin-ui/app/locales/en/translation.json index fe9c743f5..5be1180d2 100644 --- a/admin-ui/app/locales/en/translation.json +++ b/admin-ui/app/locales/en/translation.json @@ -84,7 +84,10 @@ "config_api_status":"Config API Status", "key_cloak":"Keycloak", "jans_lock":"Jans Lock", - "jans_link":"Jans Link" + "jans_link":"Jans Link", + "access_denied":"Access Denied", + "access_denied_message":"You do not have permission to access this page", + "access_contact_admin":"Please contact your administrator for more information" }, "fields": { "access_token_signing_alg": "JWS alg for signing", diff --git a/admin-ui/app/locales/fr/translation.json b/admin-ui/app/locales/fr/translation.json index 7eb8b51be..9c4584793 100644 --- a/admin-ui/app/locales/fr/translation.json +++ b/admin-ui/app/locales/fr/translation.json @@ -31,7 +31,10 @@ "config_api_status": "État de l'API de configuration", "key_cloak": "Keycloak", "jans_lock": "Jans Lock", - "jans_link": "Lien Jans" + "jans_link": "Lien Jans", + "access_denied":"Accès refusé", + "access_denied_message":"Vous n'êtes pas autorisé à accéder à cette page", + "access_contact_admin":"Veuillez contacter l'administrateur pour obtenir de l'aide" }, "menus": { "adminui": "Administratrice", diff --git a/admin-ui/app/locales/pt/translation.json b/admin-ui/app/locales/pt/translation.json index 6c717aa1a..e08ce76d5 100644 --- a/admin-ui/app/locales/pt/translation.json +++ b/admin-ui/app/locales/pt/translation.json @@ -31,7 +31,10 @@ "config_api_status": "Status da API de configuração", "key_cloak": "Keycloak", "jans_lock": "Jans Lock", - "jans_link": "Link Jans" + "jans_link": "Link Jans", + "access_denied":"Acesso negado", + "access_denied_message":"Entre em contato com o administrador para obter ajuda", + "access_contact_admin":"Se você acha que isso é um erro, entre em contato com o administrador" }, "menus": { "adminui": "Admin", diff --git a/admin-ui/app/routes/Apps/Gluu/GluuPermissionModal.js b/admin-ui/app/routes/Apps/Gluu/GluuPermissionModal.js new file mode 100644 index 000000000..457559f61 --- /dev/null +++ b/admin-ui/app/routes/Apps/Gluu/GluuPermissionModal.js @@ -0,0 +1,49 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Button, Modal, ModalBody, ModalFooter, ModalHeader } from "reactstrap"; + +const GluuPermissionModal = ({ description = "", handler, isOpen }) => { + const { t } = useTranslation(); + + return ( +
+ + + {t("dashboard.access_denied")} + + +

+ 🚫 {t("dashboard.access_denied_message")} +

+

{t("dashboard.access_contact_admin")}

+
+ + + +
+ + {/* Scoped CSS inside the component */} + +
+ ); +}; + +export default GluuPermissionModal; diff --git a/admin-ui/app/routes/Dashboards/DashboardPage.js b/admin-ui/app/routes/Dashboards/DashboardPage.js index ac991e1af..69c4480fe 100644 --- a/admin-ui/app/routes/Dashboards/DashboardPage.js +++ b/admin-ui/app/routes/Dashboards/DashboardPage.js @@ -26,6 +26,8 @@ import UsersIcon from "Components/SVG/menu/Users"; import Administrator from "Components/SVG/menu/Administrator"; import OAuthIcon from "Components/SVG/menu/OAuth"; import { getHealthServerStatus } from "../../redux/features/healthSlice"; +import GluuPermissionModal from "Routes/Apps/Gluu/GluuPermissionModal"; +import { auditLogoutLogs } from "../../../plugins/user-management/redux/features/userSlice"; function DashboardPage() { const { t } = useTranslation(); @@ -80,20 +82,22 @@ function DashboardPage() { }, [statData]); useEffect(() => { - if (Object.keys(license).length === 0 && access_token) { + if (Object.keys(license).length === 0 && access_token && hasBoth(permissions, STAT_READ, STAT_JANS_READ)) { getLicense(); } }, [access_token, license]); useEffect(() => { - if (clients.length === 0 && access_token) { + if (clients.length === 0 && access_token && hasBoth(permissions, STAT_READ, STAT_JANS_READ)) { buildPayload(userAction, "Fetch openid connect clients", {}); dispatch(getClients({ action: userAction })); } }, [access_token, clients]); useEffect(() => { - if (access_token) { + + if (access_token && hasBoth(permissions, STAT_READ, STAT_JANS_READ)) { + console.log("access_token", access_token,hasBoth(permissions, STAT_READ, STAT_JANS_READ)); getServerStatus(); buildPayload(userAction, "GET Health Status", { service: "all" }); dispatch(getHealthServerStatus({ action: userAction })); @@ -289,14 +293,23 @@ function DashboardPage() { ); }, [serverStatus, serverHealth, dbStatus, t, statusDetails, classes]); + const handleLogout = () => { + dispatch(auditLogoutLogs({ message: "Logging out due to insufficient permissions for Admin UI access." })); + }; + return ( + { + handleLogout(); + }} + isOpen={!hasBoth(permissions, STAT_READ, STAT_JANS_READ)} + />
-
- + -