diff --git a/admin-ui/app/routes/Apps/Gluu/GluuTypeAhead.js b/admin-ui/app/routes/Apps/Gluu/GluuTypeAhead.js index c36a61d51..037eb02fe 100644 --- a/admin-ui/app/routes/Apps/Gluu/GluuTypeAhead.js +++ b/admin-ui/app/routes/Apps/Gluu/GluuTypeAhead.js @@ -24,6 +24,7 @@ function GluuTypeAhead({ required, doc_category, doc_entry, + forwardRef = null, }) { const { t } = useTranslation() return ( @@ -37,6 +38,7 @@ function GluuTypeAhead({ { diff --git a/admin-ui/plugins/admin/components/Mapping/MappingItem.js b/admin-ui/plugins/admin/components/Mapping/MappingItem.js index 58dd56a5e..37a93170f 100644 --- a/admin-ui/plugins/admin/components/Mapping/MappingItem.js +++ b/admin-ui/plugins/admin/components/Mapping/MappingItem.js @@ -1,4 +1,4 @@ -import React, { useState } from 'react' +import React, { useEffect, useState, useRef } from 'react' import { Row, Badge, @@ -6,43 +6,145 @@ import { Button, FormGroup, Accordion, + Form, } from '../../../../app/components' +import { useDispatch, useSelector } from 'react-redux' +import { + updateMapping, + addPermissionsToRole, + updatePermissionsToServer, + updatePermissionsServerResponse, +} from '../../redux/actions/MappingActions' +import GluuTypeAhead from '../../../../app/routes/Apps/Gluu/GluuTypeAhead' +import applicationStyle from '../../../../app/routes/Apps/Gluu/styles/applicationstyle' + +import { Formik } from 'formik' + +function MappingItem({ candidate }) { + const dispatch = useDispatch() + const autocompleteRef = useRef(null) + const permissions = useSelector((state) => state.apiPermissionReducer.items) + const [searchablePermissions, setSearchAblePermissions] = useState([]) + const [serverPermissions, setServerPermissions] = useState(null) + const getPermissionsForSearch = () => { + const selectedPermissions = candidate.permissions + let filteredArr = [] + for (let i in permissions) { + if (!selectedPermissions.includes(permissions[i].permission)) { + filteredArr.push(permissions[i].permission) + } + } + setSearchAblePermissions(filteredArr) + } -function MappingItem({ candidate, key }) { - const [item, setItem] = useState(candidate || {}) - function doRemove(perm) { - const candidate = item - candidate['permissions'] = ['one', 'two'] - setItem(candidate) + const revertLocalChanges = () => { + dispatch(updatePermissionsServerResponse(JSON.parse(serverPermissions))) } + + const setServerPermissionsToLocalState = () => { + setServerPermissions(JSON.stringify(candidate)) + } + + useEffect(() => { + setServerPermissionsToLocalState() + }, [false]) + + useEffect(() => { + getPermissionsForSearch() + }, [permissions, candidate?.permissions?.length]) + + const doRemove = (id, role) => { + dispatch( + updateMapping({ + id, + role, + }), + ) + } + + const initialValues = {} + + const handleAddPermission = (values, { resetForm }) => { + if (values?.mappingAddPermissions?.length) { + dispatch( + addPermissionsToRole({ + data: values?.mappingAddPermissions, + userRole: candidate.role, + }), + ) + } + resetForm() + autocompleteRef.current.clear() + } + return ( -
+
- {item.role} + {candidate.role} - {item.permissions.length} + {candidate.permissions.length} - {item.permissions.map((permission, id) => ( +
+ + {(formik) => ( + <> +
+ + + + + + + + +
+ + )} +
+ {candidate.permissions.map((permission, id) => ( {permission} + + + + + + {/* Bottom Buttons */}
diff --git a/admin-ui/plugins/admin/components/Mapping/MappingPage.js b/admin-ui/plugins/admin/components/Mapping/MappingPage.js index 75a8f506e..3dbe431d4 100644 --- a/admin-ui/plugins/admin/components/Mapping/MappingPage.js +++ b/admin-ui/plugins/admin/components/Mapping/MappingPage.js @@ -12,8 +12,10 @@ import { } from '../../../../app/components' import GluuViewWrapper from '../../../../app/routes/Apps/Gluu/GluuViewWrapper' import GluuRibbon from '../../../../app/routes/Apps/Gluu/GluuRibbon' +import GluuLoader from '../../../../app/routes/Apps/Gluu/GluuLoader' import { getMapping } from '../../redux/actions/MappingActions' import { getRoles } from '../../redux/actions/ApiRoleActions' +import { getPermissions } from '../../redux/actions/ApiPermissionActions' import MappingItem from './MappingItem' import { hasPermission, @@ -21,15 +23,29 @@ import { ROLE_READ, } from '../../../../app/utils/PermChecker' -function MappingPage({ mapping, apiRoles, permissions, dispatch }) { +function MappingPage({ + mapping, + apiRoles, + permissions, + permissionLoading, + loading, + dispatch, +}) { const { t } = useTranslation() const [modal, setModal] = useState(false) const toggle = () => setModal(!modal) const options = [] const userAction = {} + + function doFetchPermissionsList() { + buildPayload(userAction, 'PERMISSIONS', options) + dispatch(getPermissions(userAction)) + } + useEffect(() => { doFetchList() doFetchRoles() + doFetchPermissionsList() }, []) function onAddConfirmed(mappingData) { @@ -51,46 +67,51 @@ function MappingPage({ mapping, apiRoles, permissions, dispatch }) { toggle() } return ( - - - - - - - - - - - - - {mapping.map((candidate, idx) => ( - - ))} - - - - + + + + + + + + + + + + + + {mapping.map((candidate, idx) => ( + + ))} + + + + + + ) } const mapStateToProps = (state) => { return { mapping: state.mappingReducer.items, + loading: state.mappingReducer.loading, apiRoles: state.apiRoleReducer.items, permissions: state.authReducer.permissions, + permissionLoading: state.apiPermissionReducer.loading, } } diff --git a/admin-ui/plugins/admin/redux/actions/MappingActions.js b/admin-ui/plugins/admin/redux/actions/MappingActions.js index d902e2e44..788eb1dfa 100644 --- a/admin-ui/plugins/admin/redux/actions/MappingActions.js +++ b/admin-ui/plugins/admin/redux/actions/MappingActions.js @@ -1,4 +1,12 @@ -import { GET_MAPPING, GET_MAPPING_RESPONSE } from './types' +import { + GET_MAPPING, + GET_MAPPING_RESPONSE, + UPDATE_MAPPING, + ADD_PERMISSIONS_TO_ROLE, + UPDATE_PERMISSIONS_TO_SERVER, + UPDATE_PERMISSIONS_LOADING, + UPDATE_PERMISSIONS_SERVER_RESPONSE, +} from './types' export const getMapping = (action) => ({ type: GET_MAPPING, @@ -9,3 +17,27 @@ export const getMappingResponse = (data) => ({ type: GET_MAPPING_RESPONSE, payload: { data }, }) + +export const updateMapping = (data) => ({ + type: UPDATE_MAPPING, + payload: { data }, +}) + +export const updatePermissionsLoading = (data) => ({ + type: UPDATE_PERMISSIONS_LOADING, + payload: { data }, +}) + +export const updatePermissionsServerResponse = (data) => ({ + type: UPDATE_PERMISSIONS_SERVER_RESPONSE, + payload: { data }, +}) +export const addPermissionsToRole = (data) => ({ + type: ADD_PERMISSIONS_TO_ROLE, + payload: { data }, +}) + +export const updatePermissionsToServer = (data) => ({ + type: UPDATE_PERMISSIONS_TO_SERVER, + payload: { data }, +}) diff --git a/admin-ui/plugins/admin/redux/actions/types.js b/admin-ui/plugins/admin/redux/actions/types.js index ff0a1836a..27a89893b 100644 --- a/admin-ui/plugins/admin/redux/actions/types.js +++ b/admin-ui/plugins/admin/redux/actions/types.js @@ -46,7 +46,12 @@ export const SET_PERMISSION_ITEM = 'SET_PERMISSION_ITEM' //ROLE-PERMISSION Mapping export const GET_MAPPING = 'GET_MAPPING' export const GET_MAPPING_RESPONSE = 'GET_MAPPING_RESPONSE' - +export const UPDATE_MAPPING = 'UPDATE_MAPPING' +export const ADD_PERMISSIONS_TO_ROLE = 'ADD_PERMISSIONS_TO_ROLE' +export const UPDATE_PERMISSIONS_TO_SERVER = 'UPDATE_PERMISSIONS_TO_SERVER' +export const UPDATE_PERMISSIONS_SERVER_RESPONSE = + 'UPDATE_PERMISSIONS_SERVER_RESPONSE' +export const UPDATE_PERMISSIONS_LOADING = 'UPDATE_PERMISSIONS_LOADING' //License Details export const GET_LICENSE_DETAILS = 'GET_LICENSE_DETAILS' diff --git a/admin-ui/plugins/admin/redux/api/MappingApi.js b/admin-ui/plugins/admin/redux/api/MappingApi.js index a36ac8b94..d43fb1aeb 100644 --- a/admin-ui/plugins/admin/redux/api/MappingApi.js +++ b/admin-ui/plugins/admin/redux/api/MappingApi.js @@ -10,6 +10,16 @@ export default class MappingApi { }) } + updateMapping = (data) => { + const options = {} + options['rolePermissionMapping'] = data + return new Promise((resolve, reject) => { + this.api.mapPermissionsToRole(options, (error, options) => { + this.handleResponse(error, reject, resolve, data) + }) + }) + } + handleResponse(error, reject, resolve, data) { if (error) { reject(error) diff --git a/admin-ui/plugins/admin/redux/reducers/MappingReducer.js b/admin-ui/plugins/admin/redux/reducers/MappingReducer.js index a500e282a..f468f6878 100644 --- a/admin-ui/plugins/admin/redux/reducers/MappingReducer.js +++ b/admin-ui/plugins/admin/redux/reducers/MappingReducer.js @@ -1,8 +1,17 @@ -import { GET_MAPPING, GET_MAPPING_RESPONSE, RESET } from '../actions/types' +import { + GET_MAPPING, + GET_MAPPING_RESPONSE, + UPDATE_MAPPING, + ADD_PERMISSIONS_TO_ROLE, + UPDATE_PERMISSIONS_LOADING, + RESET, + UPDATE_PERMISSIONS_SERVER_RESPONSE, +} from '../actions/types' import reducerRegistry from '../../../../app/redux/reducers/ReducerRegistry' const INIT_STATE = { items: [], + serverItems: [], loading: false, } const reducerName = 'mappingReducer' @@ -10,7 +19,6 @@ const reducerName = 'mappingReducer' export default function mappingReducer(state = INIT_STATE, action) { switch (action.type) { case GET_MAPPING: - console.log("==================>") return handleLoading() case GET_MAPPING_RESPONSE: if (action.payload.data) { @@ -18,7 +26,46 @@ export default function mappingReducer(state = INIT_STATE, action) { } else { return handleDefault() } - + case ADD_PERMISSIONS_TO_ROLE: + const { data, userRole } = action.payload.data + let roleIndex = state.items.findIndex( + (element) => element.role == userRole, + ) + let existingPermissions = state.items[roleIndex].permissions + let newArr = existingPermissions.concat(data) + let addedPermissions = state.items + addedPermissions[roleIndex].permissions = newArr + return { + ...state, + items: [...addedPermissions], + } + case UPDATE_PERMISSIONS_LOADING: + return { + ...state, + loading: action.payload.data, + } + case UPDATE_PERMISSIONS_SERVER_RESPONSE: + let indexToUpdatePermissions = state.items.findIndex( + (element) => element.role == action.payload?.data?.role, + ) + let changedData = state.items + changedData[indexToUpdatePermissions] = action.payload.data + return { + ...state, + items: [...changedData], + loading: false, + } + case UPDATE_MAPPING: + const { id, role } = action.payload.data + let index = state.items.findIndex((element) => element.role == role) + let permissions = state.items[index].permissions + permissions.splice(id, 1) + let changedPermissions = state.items + changedPermissions[index].permissions = permissions + return { + ...state, + items: [...changedPermissions], + } case RESET: return { ...state, diff --git a/admin-ui/plugins/admin/redux/sagas/ApiPermissionSaga.js b/admin-ui/plugins/admin/redux/sagas/ApiPermissionSaga.js index 4a455042e..1b6439e05 100644 --- a/admin-ui/plugins/admin/redux/sagas/ApiPermissionSaga.js +++ b/admin-ui/plugins/admin/redux/sagas/ApiPermissionSaga.js @@ -5,7 +5,7 @@ import PermissionApi from '../api/PermissionApi' import { getClient } from '../../../../app/redux/api/base' import { postUserAction } from '../../../../app/redux/api/backend-api' import { - getPermissionsResponse, + getPermissionResponse, addPermissionResponse, editPermissionResponse, deletePermissionResponse, @@ -46,10 +46,10 @@ export function* getPermissions({ payload }) { addAdditionalData(audit, FETCH, API_PERMISSION, payload) const permApi = yield* newFunction() const data = yield call(permApi.getPermissions) - yield put(getPermissionsResponse(data)) + yield put(getPermissionResponse(data)) yield call(postUserAction, audit) } catch (e) { - yield put(getPermissionsResponse(null)) + yield put(getPermissionResponse(null)) if (isFourZeroOneError(e)) { const jwt = yield select((state) => state.authReducer.userinfo_jwt) yield put(getAPIAccessToken(jwt)) diff --git a/admin-ui/plugins/admin/redux/sagas/MappingSaga.js b/admin-ui/plugins/admin/redux/sagas/MappingSaga.js index 5d984072a..1fd26175c 100644 --- a/admin-ui/plugins/admin/redux/sagas/MappingSaga.js +++ b/admin-ui/plugins/admin/redux/sagas/MappingSaga.js @@ -1,5 +1,17 @@ -import { call, all, put, fork, takeLatest, select } from 'redux-saga/effects' -import { getMappingResponse } from '../actions/MappingActions' +import { + call, + all, + put, + fork, + takeLatest, + select, + takeEvery, +} from 'redux-saga/effects' +import { + getMappingResponse, + updatePermissionsServerResponse, + updatePermissionsLoading, +} from '../actions/MappingActions' import { API_MAPPING } from '../audit/Resources' import { FETCH } from '../../../../app/audit/UserActionType' import { getAPIAccessToken } from '../../../../app/redux/actions/AuthActions' @@ -7,7 +19,7 @@ import { isFourZeroOneError, addAdditionalData, } from '../../../../app/utils/TokenController' -import { GET_MAPPING } from '../actions/types' +import { GET_MAPPING, UPDATE_PERMISSIONS_TO_SERVER } from '../actions/types' import MappingApi from '../api/MappingApi' import { getClient } from '../../../../app/redux/api/base' import { postUserAction } from '../../../../app/redux/api/backend-api' @@ -40,8 +52,25 @@ export function* fetchMapping({ payload }) { } } +export function* updateMapping({ payload }) { + yield put(updatePermissionsLoading(true)) + try { + const mappingApi = yield* newFunction() + const data = yield call(mappingApi.updateMapping, payload.data) + yield put(updatePermissionsServerResponse(data)) + } catch (e) { + yield put(updatePermissionsLoading(false)) + yield put(getMappingResponse(null)) + if (isFourZeroOneError(e)) { + const jwt = yield select((state) => state.authReducer.userinfo_jwt) + yield put(getAPIAccessToken(jwt)) + } + } +} + export function* watchGetMapping() { yield takeLatest(GET_MAPPING, fetchMapping) + yield takeEvery(UPDATE_PERMISSIONS_TO_SERVER, updateMapping) } export default function* rootSaga() {