Skip to content

Commit

Permalink
Merge branch 'feature/shibui-1742' of bitbucket.org:unicon/shib-idp-u…
Browse files Browse the repository at this point in the history
…i into feature/shibui-1742
  • Loading branch information
chasegawa committed Jul 29, 2021
2 parents 7f4eca8 + 43ea0b8 commit bd86f72
Show file tree
Hide file tree
Showing 26 changed files with 736 additions and 27 deletions.
19 changes: 18 additions & 1 deletion backend/src/main/resources/i18n/messages.properties
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ action.custom-entity-attributes=Custom Entity Attributes
action.enable=Enable
action.disable=Disable

action.add-new-role=Add new role
action.roles=Roles
action.source-role=Role

value.enabled=Enabled
value.disabled=Disabled
value.current=Current
Expand Down Expand Up @@ -483,6 +487,16 @@ label.by=By
label.source=Metadata Source
label.provider=Metadata Provider

label.roles-management=Role Management
label.new-role=New Role
label.role-name=Role Name
label.role-description=Role Description
label.role=Role

message.delete-role-title=Delete Role?

message.delete-role-body=You are requesting to delete a role. If you complete this process the role will be removed. This cannot be undone. Do you wish to continue?

message.delete-user-title=Delete User?
message.delete-user-body=You are requesting to delete a user. If you complete this process the user will be removed. This cannot be undone. Do you wish to continue?

Expand Down Expand Up @@ -669,4 +683,7 @@ tooltip.match=A regular expression against which the entityID is evaluated.
tooltip.remove-existing-formats=Whether to remove any existing formats from a role if any are added by the filter (unmodified roles will be untouched regardless of this setting)
tooltip.nameid-formats-format=Format
tooltip.nameid-formats-value=Value
tooltip.nameid-formats-type=Type
tooltip.nameid-formats-type=Type

tooltip.role-name=Role Name
tooltip.role-description=Role Description
15 changes: 15 additions & 0 deletions ui/public/assets/schema/roles/role.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "label.role-name",
"description": "tooltip.role-name",
"type": "string",
"minLength": 1,
"maxLength": 255
}
}
}
2 changes: 2 additions & 0 deletions ui/src/app/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { NewProvider } from './metadata/new/NewProvider';
import { Filter } from './metadata/Filter';
import { Contention } from './metadata/contention/ContentionContext';
import { SessionModal } from './core/user/SessionModal';
import { Roles } from './admin/Roles';
import Button from 'react-bootstrap/Button';


Expand Down Expand Up @@ -79,6 +80,7 @@ function App() {
<Route path="/metadata/attributes" component={Attribute} />
<Route path={`/metadata/provider/:id/filter`} component={Filter} />
<Route path="/metadata/:type/:id" component={Metadata} />
<Route path="/roles" component={Roles} />
<Route path="*">
<Redirect to="/dashboard" />
</Route>
Expand Down
32 changes: 32 additions & 0 deletions ui/src/app/admin/Roles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from 'react';
import { Switch, Route, useRouteMatch, Redirect } from 'react-router-dom';
import { RolesProvider } from './hoc/RolesProvider';
import { NewRole } from './container/NewRole';
import { EditRole } from './container/EditRole';
import { RoleList } from './container/RoleList';

export function Roles() {

let { path } = useRouteMatch();

return (
<>
<Switch>
<Route path={`${path}/list`} render={() =>
<RolesProvider>
{(roles, onDelete) =>
<RoleList roles={roles} onDelete={onDelete} />
}
</RolesProvider>
} />
<Route path={`${path}/new`} render={() =>
<NewRole />
} />
<Route path={`${path}/:id/edit`} render={() =>
<EditRole />
} />
<Redirect exact path={`${path}`} to={`${path}/list`} />
</Switch>
</>
);
}
68 changes: 68 additions & 0 deletions ui/src/app/admin/component/RoleForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from 'react';
import Button from 'react-bootstrap/Button';
import Form from '@rjsf/bootstrap-4';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faSpinner, faSave } from '@fortawesome/free-solid-svg-icons';
import Translate from '../../i18n/components/translate';

import { useRoleUiSchema } from '../hooks';
import { fields, widgets } from '../../form/component';
import { templates } from '../../form/component';
import { FormContext, setFormDataAction, setFormErrorAction } from '../../form/FormManager';

function ErrorListTemplate() {
return (<></>);
}

export function RoleForm({ role = {}, errors = [], loading = false, schema, onSave, onCancel }) {

const { dispatch } = React.useContext(FormContext);
const onChange = ({ formData, errors }) => {
dispatch(setFormDataAction(formData));
dispatch(setFormErrorAction(errors));
};

const uiSchema = useRoleUiSchema();

return (<>
<div className="container-fluid">
<div className="d-flex justify-content-end align-items-center">
<React.Fragment>
<Button variant="info" className="mr-2"
type="button"
onClick={() => onSave(role)}
disabled={errors.length > 0 || loading}
aria-label="Save changes to the metadata source. You will return to the dashboard">
<FontAwesomeIcon icon={loading ? faSpinner : faSave} pulse={loading} />&nbsp;
<Translate value="action.save">Save</Translate>
</Button>
<Button variant="secondary"
type="button"
onClick={() => onCancel()} aria-label="Cancel changes, go back to dashboard">
<Translate value="action.cancel">Cancel</Translate>
</Button>
</React.Fragment>
</div>
<hr />
<div className="row">
<div className="col-12 col-lg-6 order-2">
<Form formData={role}
noHtml5Validate={true}
onChange={(form) => onChange(form)}
schema={schema}
uiSchema={uiSchema}
FieldTemplate={templates.FieldTemplate}
ObjectFieldTemplate={templates.ObjectFieldTemplate}
ArrayFieldTemplate={templates.ArrayFieldTemplate}
fields={fields}
widgets={widgets}
liveValidate={true}
ErrorList={ErrorListTemplate}>
<></>
</Form>
</div>
</div>
</div>
</>)
}
/**/
91 changes: 91 additions & 0 deletions ui/src/app/admin/container/EditRole.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import React from 'react';

import { Prompt, useHistory } from 'react-router';
import { useParams } from 'react-router-dom';
import Translate from '../../i18n/components/translate';
import { useRoles } from '../hooks';
import { Schema } from '../../form/Schema';
import { FormManager } from '../../form/FormManager';

import { RoleForm } from '../component/RoleForm';
import { RoleProvider } from '../hoc/RoleProvider';
import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';

export function EditRole() {

const { id } = useParams();

const notifier = useNotificationDispatcher();
const translator = useTranslator();

const history = useHistory();

const { put, response, loading } = useRoles();

const [blocking, setBlocking] = React.useState(false);

async function save(role) {
let toast;
const resp = await put(``, role);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Updated role successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

const cancel = () => {
gotoDetail();
};

const gotoDetail = (state = null) => {
setBlocking(false);
history.push(`/roles`, state);
};

return (
<div className="container-fluid p-3">
<Prompt
when={blocking}
message={location =>
`message.unsaved-editor`
}
/>
<section className="section" tabIndex="0">
<div className="section-header bg-info p-2 text-white">
<div className="row justify-content-between">
<div className="col-md-12">
<span className="display-6"><Translate value="label.edit-role">Edit role</Translate></span>
</div>
</div>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<RoleProvider id={id}>
{(role) =>
<Schema path={`/assets/schema/roles/role.json`}>
{(schema) =>
<>{role &&
<FormManager initial={role}>
{(data, errors) =>
<RoleForm
role={data}
errors={errors}
schema={schema}
loading={loading}
onSave={(data) => save(data)}
onCancel={() => cancel()} />}
</FormManager>
}</>}
</Schema>
}
</RoleProvider>
</div>
</section>
</div>
);
}
2 changes: 1 addition & 1 deletion ui/src/app/admin/container/MetadataActions.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { DeleteConfirmation } from '../../metadata/component/DeleteConfirmation';
import { DeleteConfirmation } from '../../core/components/DeleteConfirmation';
import { useMetadataEntity } from '../../metadata/hooks/api';

import { NotificationContext, createNotificationAction } from '../../notifications/hoc/Notifications';
Expand Down
79 changes: 79 additions & 0 deletions ui/src/app/admin/container/NewRole.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React from 'react';

import { Prompt, useHistory } from 'react-router';
import Translate from '../../i18n/components/translate';
import { useRoles } from '../hooks';
import { Schema } from '../../form/Schema';
import { FormManager } from '../../form/FormManager';
import { RoleForm } from '../component/RoleForm';

import { createNotificationAction, NotificationTypes, useNotificationDispatcher } from '../../notifications/hoc/Notifications';
import { useTranslator } from '../../i18n/hooks';

export function NewRole() {
const history = useHistory();
const notifier = useNotificationDispatcher();
const translator = useTranslator();

const { post, response, loading } = useRoles({});

const [blocking, setBlocking] = React.useState(false);

async function save(role) {
let toast;
const resp = await post(``, role);
if (response.ok) {
gotoDetail({ refresh: true });
toast = createNotificationAction(`Added role successfully.`, NotificationTypes.SUCCESS);
} else {
toast = createNotificationAction(`${resp.errorCode} - ${translator(resp.errorMessage)}`, NotificationTypes.ERROR);
}
if (toast) {
notifier(toast);
}
};

const cancel = () => {
gotoDetail();
};

const gotoDetail = (state = null) => {
setBlocking(false);
history.push(`/roles`, state);
};

return (
<div className="container-fluid p-3">
<Prompt
when={blocking}
message={location =>
`message.unsaved-editor`
}
/>
<section className="section" tabIndex="0">
<div className="section-header bg-info p-2 text-white">
<div className="row justify-content-between">
<div className="col-md-12">
<span className="display-6"><Translate value="label.new-role">Add a new role</Translate></span>
</div>
</div>
</div>
<div className="section-body p-4 border border-top-0 border-info">
<Schema path={`/assets/schema/roles/role.json`}>
{(schema) =>
<FormManager initial={{}}>
{(data, errors) =>
<RoleForm
role={data}
errors={errors}
schema={schema}
loading={loading}
onSave={(data) => save(data)}
onCancel={() => cancel()} />}
</FormManager>}
</Schema>
</div>
</section>
</div>
);
}
Loading

0 comments on commit bd86f72

Please sign in to comment.