mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
fix: make admin pages work for OSS and enterprise (#268)
* fix: make admin pages work for OSS and enterprise * fix: more admin tuning * fix: project mgm access
This commit is contained in:
parent
2a0acd3fb2
commit
b3436b5ae6
@ -93,6 +93,15 @@ Array [
|
||||
"title": "Sign out",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"hidden": false,
|
||||
"icon": "album",
|
||||
"layout": "main",
|
||||
"path": "/admin",
|
||||
"title": "Admin",
|
||||
"type": "protected",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
@ -260,39 +269,6 @@ Array [
|
||||
"title": "Projects",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/api",
|
||||
"title": "API access",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/users",
|
||||
"title": "Users",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/auth",
|
||||
"title": "Authentication",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"hidden": true,
|
||||
"icon": "album",
|
||||
"layout": "main",
|
||||
"path": "/admin",
|
||||
"title": "Admin",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
@ -389,5 +365,43 @@ Array [
|
||||
"title": "Log in",
|
||||
"type": "unprotected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/api",
|
||||
"title": "API access",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/users",
|
||||
"title": "Users",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": Object {
|
||||
"$$typeof": Symbol(react.memo),
|
||||
"WrappedComponent": [Function],
|
||||
"compare": null,
|
||||
"type": [Function],
|
||||
},
|
||||
"layout": "main",
|
||||
"parent": "/admin",
|
||||
"path": "/admin/auth",
|
||||
"title": "Authentication",
|
||||
"type": "protected",
|
||||
},
|
||||
Object {
|
||||
"component": [Function],
|
||||
"hidden": false,
|
||||
"icon": "album",
|
||||
"layout": "main",
|
||||
"path": "/admin",
|
||||
"title": "Admin",
|
||||
"type": "protected",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -6,7 +6,7 @@ test('returns all defined routes', () => {
|
||||
});
|
||||
|
||||
test('returns all baseRoutes', () => {
|
||||
expect(baseRoutes.length).toEqual(11);
|
||||
expect(baseRoutes.length).toEqual(12);
|
||||
expect(baseRoutes).toMatchSnapshot();
|
||||
});
|
||||
|
||||
|
@ -213,41 +213,6 @@ export const routes = [
|
||||
layout: 'main',
|
||||
},
|
||||
|
||||
// Admin
|
||||
{
|
||||
path: '/admin/api',
|
||||
parent: '/admin',
|
||||
title: 'API access',
|
||||
component: AdminApi,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin/users',
|
||||
parent: '/admin',
|
||||
title: 'Users',
|
||||
component: AdminUsers,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin/auth',
|
||||
parent: '/admin',
|
||||
title: 'Authentication',
|
||||
component: AdminAuth,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
title: 'Admin',
|
||||
icon: 'album',
|
||||
component: Admin,
|
||||
hidden: true,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
|
||||
{
|
||||
path: '/tag-types/create',
|
||||
parent: '/tag-types',
|
||||
@ -342,6 +307,40 @@ export const routes = [
|
||||
hidden: true,
|
||||
layout: 'standalone',
|
||||
},
|
||||
// Admin
|
||||
{
|
||||
path: '/admin/api',
|
||||
parent: '/admin',
|
||||
title: 'API access',
|
||||
component: AdminApi,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin/users',
|
||||
parent: '/admin',
|
||||
title: 'Users',
|
||||
component: AdminUsers,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin/auth',
|
||||
parent: '/admin',
|
||||
title: 'Authentication',
|
||||
component: AdminAuth,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
title: 'Admin',
|
||||
icon: 'album',
|
||||
component: Admin,
|
||||
hidden: false,
|
||||
type: 'protected',
|
||||
layout: 'main',
|
||||
},
|
||||
];
|
||||
|
||||
export const getRoute = path => routes.find(route => route.path === path);
|
||||
|
@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import HeaderTitle from '../../common/HeaderTitle';
|
||||
import ConditionallyRender from '../../common/ConditionallyRender/ConditionallyRender';
|
||||
import { CREATE_PROJECT, DELETE_PROJECT } from '../../../permissions';
|
||||
import { CREATE_PROJECT, DELETE_PROJECT, UPDATE_PROJECT } from '../../../permissions';
|
||||
import { Icon, IconButton, List, ListItem, ListItemAvatar, ListItemText, Tooltip } from '@material-ui/core';
|
||||
import { Link } from 'react-router-dom';
|
||||
import ConfirmDialogue from '../../common/Dialogue';
|
||||
@ -36,6 +36,16 @@ const ProjectList = ({ projects, fetchProjects, removeProject, history, hasPermi
|
||||
</Link>
|
||||
);
|
||||
|
||||
const mgmAccessButton = project => (
|
||||
<Tooltip title="Manage access">
|
||||
<Link to={`/projects/${project.id}/access`} style={{ color: 'black' }}>
|
||||
<IconButton aria-label="manage_access" >
|
||||
<Icon>supervised_user_circle</Icon>
|
||||
</IconButton>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const deleteProjectButton = project => (
|
||||
<Tooltip title="Remove project">
|
||||
<IconButton
|
||||
@ -57,12 +67,16 @@ const ProjectList = ({ projects, fetchProjects, removeProject, history, hasPermi
|
||||
<Icon>folder_open</Icon>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={projectLink(project)} secondary={project.description} />
|
||||
<ConditionallyRender
|
||||
condition={hasPermission(UPDATE_PROJECT)}
|
||||
show={mgmAccessButton(project)}
|
||||
/>
|
||||
<ConditionallyRender condition={hasPermission(DELETE_PROJECT)} show={deleteProjectButton(project)} />
|
||||
</ListItem>
|
||||
));
|
||||
|
||||
return (
|
||||
<PageContent headerContent={<HeaderTitle title="Projects (beta)" actions={addProjectButton()} />}>
|
||||
<PageContent headerContent={<HeaderTitle title="Projects" actions={addProjectButton()} />}>
|
||||
<List>
|
||||
<ConditionallyRender
|
||||
condition={projects.length > 0}
|
||||
@ -93,8 +107,6 @@ ProjectList.propTypes = {
|
||||
removeProject: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default ProjectList;
|
||||
|
@ -1,16 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { fetchProjects, removeProject } from '../../../store/project/actions';
|
||||
import { hasPermission } from '../../../permissions';
|
||||
import { RBAC } from '../../common/flags';
|
||||
import ProjectList from './ProjectList';
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const projects = state.projects.toJS();
|
||||
const rbacEnabled = !!state.uiConfig.toJS().flags[RBAC];
|
||||
|
||||
return {
|
||||
projects,
|
||||
rbacEnabled,
|
||||
hasPermission: hasPermission.bind(null, state.user.get('profile')),
|
||||
};
|
||||
};
|
||||
|
@ -1,96 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { List, ListItem, IconButton, Icon, Paper, ListItemAvatar, ListItemText, Tooltip } from '@material-ui/core';
|
||||
import { HeaderTitle, styles as commonStyles } from '../common';
|
||||
import { CREATE_PROJECT, DELETE_PROJECT } from '../../permissions';
|
||||
import ConditionallyRender from '../common/conditionally-render';
|
||||
|
||||
class ProjectListComponent extends Component {
|
||||
static propTypes = {
|
||||
projects: PropTypes.array.isRequired,
|
||||
fetchProjects: PropTypes.func.isRequired,
|
||||
removeProject: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
rbacEnabled: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchProjects();
|
||||
}
|
||||
|
||||
removeProject = (project, evt) => {
|
||||
evt.preventDefault();
|
||||
this.props.removeProject(project);
|
||||
};
|
||||
|
||||
projectLink = ({ id, name }) => (
|
||||
<Link to={`/projects/edit/${id}`}>
|
||||
<strong>{name}</strong>
|
||||
</Link>
|
||||
);
|
||||
|
||||
deleteProjectButton = project => (
|
||||
<Tooltip title="Remove project">
|
||||
<IconButton aria-label="delete" onClick={this.removeProject.bind(this, project)}>
|
||||
<Icon>delete</Icon>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
projectList = () => {
|
||||
const { projects, hasPermission } = this.props;
|
||||
return projects.map((project, i) => (
|
||||
<ListItem key={i}>
|
||||
<ListItemAvatar>
|
||||
<Icon>folder_open</Icon>
|
||||
</ListItemAvatar>
|
||||
<ListItemText primary={this.projectLink(project)} secondary={project.description} />
|
||||
<ConditionallyRender
|
||||
condition={hasPermission(DELETE_PROJECT)}
|
||||
show={this.deleteProjectButton(project)}
|
||||
/>
|
||||
</ListItem>
|
||||
));
|
||||
};
|
||||
|
||||
addProjectButton = () => {
|
||||
const { hasPermission } = this.props;
|
||||
return (
|
||||
<ConditionallyRender
|
||||
condition={hasPermission(CREATE_PROJECT)}
|
||||
show={
|
||||
<Tooltip title="Add new project">
|
||||
<IconButton
|
||||
aria-label="add-project"
|
||||
onClick={() => this.props.history.push('/projects/create')}
|
||||
>
|
||||
<Icon>add</Icon>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { projects } = this.props;
|
||||
|
||||
return (
|
||||
<Paper shadow={0} className={commonStyles.fullwidth}>
|
||||
<HeaderTitle title="Projects (beta)" actions={this.addProjectButton()} />
|
||||
<List>
|
||||
<ConditionallyRender
|
||||
condition={projects.length > 0}
|
||||
show={this.projectList()}
|
||||
elseShow={<ListItem>No projects defined</ListItem>}
|
||||
/>
|
||||
</List>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ProjectListComponent;
|
@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { NavLink } from 'react-router-dom';
|
||||
import { Grid, Icon } from '@material-ui/core';
|
||||
import PageContent from '../../component/common/PageContent/PageContent';
|
||||
import { Paper, Icon, Tabs, Tab } from '@material-ui/core';
|
||||
|
||||
const navLinkStyle = {
|
||||
display: 'flex',
|
||||
@ -23,30 +22,36 @@ const iconStyle = {
|
||||
marginRight: '5px',
|
||||
};
|
||||
|
||||
function AdminMenu() {
|
||||
function AdminMenu({history}) {
|
||||
const { location } = history;
|
||||
const { pathname } = location;
|
||||
return (
|
||||
<PageContent style={{ marginBottom: '1rem' }}>
|
||||
<Grid container justify={'center'}>
|
||||
<Grid item md={4}>
|
||||
<Paper style={{ marginBottom: '1rem' }}>
|
||||
<Tabs centered value={pathname} >
|
||||
<Tab value="/admin/users" label={
|
||||
<NavLink to="/admin/users" activeStyle={activeNavLinkStyle} style={navLinkStyle}>
|
||||
<Icon style={iconStyle}>supervised_user_circle</Icon>
|
||||
Users
|
||||
</NavLink>
|
||||
</Grid>
|
||||
<Grid item md={4}>
|
||||
<Icon style={iconStyle}>supervised_user_circle</Icon>
|
||||
<span>Users</span>
|
||||
</NavLink>
|
||||
}
|
||||
>
|
||||
</Tab>
|
||||
<Tab value="/admin/api" label={
|
||||
<NavLink to="/admin/api" activeStyle={activeNavLinkStyle} style={navLinkStyle}>
|
||||
<Icon style={iconStyle}>apps</Icon>
|
||||
API Access
|
||||
</NavLink>
|
||||
</Grid>
|
||||
<Grid item md={4}>
|
||||
}>
|
||||
</Tab>
|
||||
<Tab value="/admin/auth" label={
|
||||
<NavLink to="/admin/auth" activeStyle={activeNavLinkStyle} style={navLinkStyle}>
|
||||
<Icon style={iconStyle}>lock</Icon>
|
||||
Authentication
|
||||
</NavLink>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</PageContent>
|
||||
}>
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</Paper>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,25 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
function ApiHowTo() {
|
||||
return (
|
||||
<div style={{ marginBottom: '1rem' }}>
|
||||
<p
|
||||
style={{
|
||||
backgroundColor: '#cfe5ff',
|
||||
border: '2px solid #c4e1ff',
|
||||
padding: '8px',
|
||||
borderRadius: '5px',
|
||||
}}
|
||||
>
|
||||
Read the{' '}
|
||||
<a href="https://www.unleash-hosted.com/docs" target="_blank" rel="noreferrer">
|
||||
Getting started guide
|
||||
</a>{' '}
|
||||
to learn how to connect to the Unleash API form your application or programmatically. <br /> <br />
|
||||
Please note it can take up to 1 minute before a new API key is activated.
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default ApiHowTo;
|
@ -7,6 +7,7 @@ import { hasPermission } from '../../../permissions';
|
||||
export default connect(
|
||||
state => ({
|
||||
location: state.settings.toJS().location || {},
|
||||
unleashUrl: state.uiConfig.toJS().unleashUrl,
|
||||
keys: state.apiAdmin.toJS(),
|
||||
hasPermission: permission => hasPermission(state.user.get('profile'), permission),
|
||||
}),
|
||||
|
@ -2,14 +2,14 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Icon, Table, TableHead, TableBody, TableRow, TableCell, IconButton } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import { formatFullDateTimeWithLocale } from '../../../component/common/util';
|
||||
import CreateApiKey from './api-key-create';
|
||||
import Secret from './secret';
|
||||
import ApiHowTo from './api-howto';
|
||||
import ConditionallyRender from '../../../component/common/ConditionallyRender/ConditionallyRender';
|
||||
import Dialogue from '../../../component/common/Dialogue/Dialogue';
|
||||
|
||||
function ApiKeyList({ location, fetchApiKeys, removeKey, addKey, keys, hasPermission }) {
|
||||
function ApiKeyList({ location, fetchApiKeys, removeKey, addKey, keys, hasPermission, unleashUrl }) {
|
||||
const [showDelete, setShowDelete] = useState(false);
|
||||
const [delKey, setDelKey] = useState(undefined);
|
||||
const deleteKey = async () => {
|
||||
@ -25,7 +25,22 @@ function ApiKeyList({ location, fetchApiKeys, removeKey, addKey, keys, hasPermis
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ApiHowTo />
|
||||
<Alert severity="info" >
|
||||
<p>
|
||||
Read the{' '}
|
||||
<a href="https://docs.getunleash.io/docs" target="_blank" rel="noreferrer">
|
||||
Getting started guide
|
||||
</a>{' '}
|
||||
to learn how to connect to the Unleash API form your application or programmatically.
|
||||
Please note it can take up to 1 minute before a new API key is activated.
|
||||
</p>
|
||||
<br />
|
||||
<strong>API URL: </strong> <pre style={{display: 'inline'}}>{unleashUrl}/api/</pre>
|
||||
</Alert>
|
||||
|
||||
<br /><br />
|
||||
|
||||
<br />
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
@ -93,6 +108,7 @@ ApiKeyList.propTypes = {
|
||||
removeKey: PropTypes.func.isRequired,
|
||||
addKey: PropTypes.func.isRequired,
|
||||
keys: PropTypes.array.isRequired,
|
||||
unleashUrl: PropTypes.string,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
@ -5,9 +5,9 @@ import ApiKeyList from './api-key-list-container';
|
||||
import AdminMenu from '../admin-menu';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
|
||||
const render = () => (
|
||||
const render = ({history}) => (
|
||||
<div>
|
||||
<AdminMenu />
|
||||
<AdminMenu history={history} />
|
||||
<PageContent headerContent="API Access">
|
||||
<ApiKeyList />
|
||||
</PageContent>
|
||||
|
56
frontend/src/page/admin/auth/authentication.jsx
Normal file
56
frontend/src/page/admin/auth/authentication.jsx
Normal file
@ -0,0 +1,56 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AdminMenu from '../admin-menu';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import GoogleAuth from './google-auth-container';
|
||||
import SamlAuth from './saml-auth-container';
|
||||
import TabNav from '../../../component/common/TabNav/TabNav';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
import ConditionallyRender from '../../../component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
function AdminAuthPage({ authenticationType, history }) {
|
||||
const tabs = [
|
||||
{
|
||||
label: 'SAML 2.0',
|
||||
component: <SamlAuth />,
|
||||
},
|
||||
{
|
||||
label: 'Google',
|
||||
component: <GoogleAuth />,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AdminMenu history={history} />
|
||||
<PageContent headerContent="Authentication">
|
||||
<ConditionallyRender condition={authenticationType === 'enterprise'}
|
||||
show={
|
||||
<TabNav tabData={tabs} />
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender condition={authenticationType === 'open-source'}
|
||||
show={
|
||||
<Alert severity="warning">
|
||||
You are running the open-source version of Unleash. You have to use the Enterprise edition
|
||||
in order configure Single Sign-on.</Alert>
|
||||
}
|
||||
/>
|
||||
<ConditionallyRender condition={authenticationType === 'custom'}
|
||||
show={
|
||||
<Alert severity="warning">You have decided to use custom authentication type. You have to use the Enterprise edition
|
||||
in order configure Single Sign-on from the user interface.</Alert>
|
||||
}
|
||||
/>
|
||||
</PageContent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
AdminAuthPage.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
authenticationType: PropTypes.string,
|
||||
};
|
||||
|
||||
export default AdminAuthPage;
|
@ -5,6 +5,7 @@ import { hasPermission } from '../../../permissions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
config: state.authAdmin.get('google'),
|
||||
unleashUrl: state.uiConfig.toJS().unleashUrl,
|
||||
hasPermission: permission => hasPermission(state.user.get('profile'), permission),
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Grid, Switch, TextField, Typography } from '@material-ui/core';
|
||||
import { Button, Grid, Switch, TextField } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
|
||||
const initialState = {
|
||||
@ -9,7 +10,7 @@ const initialState = {
|
||||
unleashHostname: location.hostname,
|
||||
};
|
||||
|
||||
function GoogleAuth({ config, getGoogleConfig, updateGoogleConfig, hasPermission }) {
|
||||
function GoogleAuth({ config, getGoogleConfig, updateGoogleConfig, hasPermission, unleashUrl }) {
|
||||
const [data, setData] = useState(initialState);
|
||||
const [info, setInfo] = useState();
|
||||
|
||||
@ -58,14 +59,14 @@ function GoogleAuth({ config, getGoogleConfig, updateGoogleConfig, hasPermission
|
||||
<PageContent>
|
||||
<Grid container style={{ marginBottom: '1rem' }}>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="subtitle1">
|
||||
<Alert severity="info">
|
||||
Please read the{' '}
|
||||
<a href="https://www.unleash-hosted.com/docs/enterprise-authentication/google" target="_blank" rel="noreferrer">
|
||||
documentation
|
||||
</a>{' '}
|
||||
to learn how to integrate with Google OAuth 2.0. <br />
|
||||
Callback URL: <code>https://[unleash.hostname.com]/auth/google/callback</code>
|
||||
</Typography>
|
||||
Callback URL: <code>{unleashUrl}/auth/google/callback</code>
|
||||
</Alert>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<form onSubmit={onSubmit}>
|
||||
@ -189,6 +190,7 @@ function GoogleAuth({ config, getGoogleConfig, updateGoogleConfig, hasPermission
|
||||
|
||||
GoogleAuth.propTypes = {
|
||||
config: PropTypes.object,
|
||||
unleashUrl: PropTypes.string,
|
||||
getGoogleConfig: PropTypes.func.isRequired,
|
||||
updateGoogleConfig: PropTypes.func.isRequired,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
|
@ -1,36 +1,12 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import AdminMenu from '../admin-menu';
|
||||
import GoogleAuth from './google-auth-container';
|
||||
import SamlAuth from './saml-auth-container';
|
||||
import TabNav from '../../../component/common/TabNav/TabNav';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
import { connect } from 'react-redux';
|
||||
import component from './authentication';
|
||||
import { hasPermission } from '../../../permissions';
|
||||
|
||||
function AdminAuthPage() {
|
||||
const tabs = [
|
||||
{
|
||||
label: 'SAML 2.0',
|
||||
component: <SamlAuth />,
|
||||
},
|
||||
{
|
||||
label: 'Google',
|
||||
component: <GoogleAuth />,
|
||||
},
|
||||
];
|
||||
const mapStateToProps = state => ({
|
||||
authenticationType: state.uiConfig.toJS().authenticationType,
|
||||
hasPermission: permission => hasPermission(state.user.get('profile'), permission),
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<AdminMenu />
|
||||
<PageContent headerContent="Authentication">
|
||||
<TabNav tabData={tabs} />
|
||||
</PageContent>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const Container = connect(mapStateToProps, { })(component);
|
||||
|
||||
AdminAuthPage.propTypes = {
|
||||
match: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default AdminAuthPage;
|
||||
export default Container;
|
||||
|
@ -5,6 +5,7 @@ import { hasPermission } from '../../../permissions';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
config: state.authAdmin.get('saml'),
|
||||
unleashUrl: state.uiConfig.toJS().unleashUrl,
|
||||
hasPermission: permission => hasPermission(state.user.get('profile'), permission),
|
||||
});
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Grid, Switch, TextField, Typography } from '@material-ui/core';
|
||||
import { Button, Grid, Switch, TextField } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
|
||||
const initialState = {
|
||||
@ -9,7 +10,7 @@ const initialState = {
|
||||
unleashHostname: location.hostname,
|
||||
};
|
||||
|
||||
function SamlAuth({ config, getSamlConfig, updateSamlConfig, hasPermission }) {
|
||||
function SamlAuth({ config, getSamlConfig, updateSamlConfig, hasPermission, unleashUrl }) {
|
||||
const [data, setData] = useState(initialState);
|
||||
const [info, setInfo] = useState();
|
||||
|
||||
@ -26,7 +27,7 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, hasPermission }) {
|
||||
}, [config]);
|
||||
|
||||
if (!hasPermission('ADMIN')) {
|
||||
return <span>You need admin privileges to access this section.</span>;
|
||||
return <Alert severity="error">You need to be a root admin to access this section.</Alert>;
|
||||
}
|
||||
|
||||
const updateField = e => {
|
||||
@ -59,14 +60,14 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, hasPermission }) {
|
||||
<PageContent>
|
||||
<Grid container style={{ marginBottom: '1rem' }}>
|
||||
<Grid item md={12}>
|
||||
<Typography variant="subtitle1">
|
||||
<Alert severity="info">
|
||||
Please read the{' '}
|
||||
<a href="https://www.unleash-hosted.com/docs/enterprise-authentication" target="_blank" rel="noreferrer">
|
||||
documentation
|
||||
</a>{' '}
|
||||
to learn how to integrate with specific SAML 2.0 providers (Okta, Keycloak, etc). <br />
|
||||
Callback URL: <code>https://[unleash.hostname.com]/auth/saml/callback</code>
|
||||
</Typography>
|
||||
Callback URL: <code>{unleashUrl}/auth/saml/callback</code>
|
||||
</Alert>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<form onSubmit={onSubmit}>
|
||||
@ -184,6 +185,7 @@ function SamlAuth({ config, getSamlConfig, updateSamlConfig, hasPermission }) {
|
||||
|
||||
SamlAuth.propTypes = {
|
||||
config: PropTypes.object,
|
||||
unleash: PropTypes.string,
|
||||
getSamlConfig: PropTypes.func.isRequired,
|
||||
updateSamlConfig: PropTypes.func.isRequired,
|
||||
hasPermission: PropTypes.func.isRequired,
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* eslint-disable no-alert */
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Icon, IconButton, Table, TableBody, TableCell, TableHead, TableRow } from '@material-ui/core';
|
||||
import { formatFullDateTimeWithLocale } from '../../../../component/common/util';
|
||||
import { Button, Icon, IconButton, Table, TableBody, TableCell, TableHead, TableRow, Avatar } from '@material-ui/core';
|
||||
import { formatDateWithLocale } from '../../../../component/common/util';
|
||||
import AddUser from '../add-user-component';
|
||||
import ChangePassword from '../change-password-component';
|
||||
import UpdateUser from '../update-user-component';
|
||||
@ -78,26 +78,26 @@ function UsersList({
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell>Id</TableCell>
|
||||
<TableCell></TableCell>
|
||||
<TableCell>Created</TableCell>
|
||||
<TableCell>Username</TableCell>
|
||||
<TableCell>Name</TableCell>
|
||||
<TableCell>Role</TableCell>
|
||||
<TableCell>{hasPermission('ADMIN') ? 'Action' : ''}</TableCell>
|
||||
<TableCell>Username</TableCell>
|
||||
<TableCell align="center">Role</TableCell>
|
||||
<TableCell align="right">{hasPermission('ADMIN') ? 'Action' : ''}</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{users.map(item => (
|
||||
<TableRow key={item.id}>
|
||||
<TableCell>{item.id}</TableCell>
|
||||
<TableCell>{formatFullDateTimeWithLocale(item.createdAt, location.locale)}</TableCell>
|
||||
<TableCell style={{ textAlign: 'left' }}>{item.username || item.email}</TableCell>
|
||||
<TableCell><Avatar variant="rounded" alt={item.name} src={item.imageUrl} title={`${item.name || item.email || item.username} (id: ${item.id})`} /></TableCell>
|
||||
<TableCell>{formatDateWithLocale(item.createdAt, location.locale)}</TableCell>
|
||||
<TableCell style={{ textAlign: 'left' }}>{item.name}</TableCell>
|
||||
<TableCell>{renderRole(item.rootRole)}</TableCell>
|
||||
<TableCell style={{ textAlign: 'left' }}>{item.username || item.email}</TableCell>
|
||||
<TableCell align="center">{renderRole(item.rootRole)}</TableCell>
|
||||
<ConditionallyRender
|
||||
condition={hasPermission('ADMIN')}
|
||||
show={
|
||||
<TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton aria-label="Edit" title="Edit" onClick={openUpdateDialog(item)}>
|
||||
<Icon>edit</Icon>
|
||||
</IconButton>
|
||||
|
@ -4,9 +4,9 @@ import UsersList from './UsersList';
|
||||
import AdminMenu from '../admin-menu';
|
||||
import PageContent from '../../../component/common/PageContent/PageContent';
|
||||
|
||||
const render = () => (
|
||||
const render = ({history}) => (
|
||||
<div>
|
||||
<AdminMenu />
|
||||
<AdminMenu history={history} />
|
||||
<PageContent headerContent="Users">
|
||||
<UsersList />
|
||||
</PageContent>
|
||||
|
Loading…
Reference in New Issue
Block a user