diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
index bcc21a42e7..5cf0b5802c 100644
--- a/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
+++ b/frontend/src/component/menu/__tests__/__snapshots__/routes-test.jsx.snap
@@ -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",
+ },
]
`;
diff --git a/frontend/src/component/menu/__tests__/routes-test.jsx b/frontend/src/component/menu/__tests__/routes-test.jsx
index ef62a7490e..44effad700 100644
--- a/frontend/src/component/menu/__tests__/routes-test.jsx
+++ b/frontend/src/component/menu/__tests__/routes-test.jsx
@@ -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();
});
diff --git a/frontend/src/component/menu/routes.js b/frontend/src/component/menu/routes.js
index 06c9937b82..9bf18d5928 100644
--- a/frontend/src/component/menu/routes.js
+++ b/frontend/src/component/menu/routes.js
@@ -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);
diff --git a/frontend/src/component/project/ProjectList/ProjectList.jsx b/frontend/src/component/project/ProjectList/ProjectList.jsx
index 40a53dd9a9..19f355d4c2 100644
--- a/frontend/src/component/project/ProjectList/ProjectList.jsx
+++ b/frontend/src/component/project/ProjectList/ProjectList.jsx
@@ -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
);
+ const mgmAccessButton = project => (
+
+
+
+ supervised_user_circle
+
+
+
+ );
+
const deleteProjectButton = project => (
folder_open
+
));
return (
- }>
+ }>
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;
diff --git a/frontend/src/component/project/ProjectList/index.jsx b/frontend/src/component/project/ProjectList/index.jsx
index c750d7bf18..f562a7764b 100644
--- a/frontend/src/component/project/ProjectList/index.jsx
+++ b/frontend/src/component/project/ProjectList/index.jsx
@@ -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')),
};
};
diff --git a/frontend/src/component/project/list-component.jsx b/frontend/src/component/project/list-component.jsx
deleted file mode 100644
index d60547a1b7..0000000000
--- a/frontend/src/component/project/list-component.jsx
+++ /dev/null
@@ -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 }) => (
-
- {name}
-
- );
-
- deleteProjectButton = project => (
-
-
- delete
-
-
- );
-
- projectList = () => {
- const { projects, hasPermission } = this.props;
- return projects.map((project, i) => (
-
-
- folder_open
-
-
-
-
- ));
- };
-
- addProjectButton = () => {
- const { hasPermission } = this.props;
- return (
-
- this.props.history.push('/projects/create')}
- >
- add
-
-
- }
- />
- );
- };
-
- render() {
- const { projects } = this.props;
-
- return (
-
-
-
- 0}
- show={this.projectList()}
- elseShow={No projects defined }
- />
-
-
- );
- }
-}
-
-export default ProjectListComponent;
diff --git a/frontend/src/page/admin/admin-menu.jsx b/frontend/src/page/admin/admin-menu.jsx
index 7ea0eaeb6e..91c73ee67d 100644
--- a/frontend/src/page/admin/admin-menu.jsx
+++ b/frontend/src/page/admin/admin-menu.jsx
@@ -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 (
-
-
-
+
+
+
- supervised_user_circle
- Users
-
-
-
+ supervised_user_circle
+ Users
+
+ }
+ >
+
+
apps
API Access
-
-
+ }>
+
+
lock
Authentication
-
-
-
+ }>
+
+
+
);
}
diff --git a/frontend/src/page/admin/api/api-howto.jsx b/frontend/src/page/admin/api/api-howto.jsx
deleted file mode 100644
index 47f59ecab6..0000000000
--- a/frontend/src/page/admin/api/api-howto.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-
-function ApiHowTo() {
- return (
-
-
- Read the{' '}
-
- Getting started guide
- {' '}
- 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.
-
-
- );
-}
-
-export default ApiHowTo;
diff --git a/frontend/src/page/admin/api/api-key-list-container.js b/frontend/src/page/admin/api/api-key-list-container.js
index 6988d6d963..ec1553ab8a 100644
--- a/frontend/src/page/admin/api/api-key-list-container.js
+++ b/frontend/src/page/admin/api/api-key-list-container.js
@@ -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),
}),
diff --git a/frontend/src/page/admin/api/api-key-list.jsx b/frontend/src/page/admin/api/api-key-list.jsx
index 888d7439fc..94e7e00c0a 100644
--- a/frontend/src/page/admin/api/api-key-list.jsx
+++ b/frontend/src/page/admin/api/api-key-list.jsx
@@ -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 (
-
+
+
+ Read the{' '}
+
+ Getting started guide
+ {' '}
+ 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.
+
+
+ API URL: {unleashUrl}/api/
+
+
+
+
+
@@ -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,
};
diff --git a/frontend/src/page/admin/api/index.js b/frontend/src/page/admin/api/index.js
index 8deb67c81d..4d6ada91cc 100644
--- a/frontend/src/page/admin/api/index.js
+++ b/frontend/src/page/admin/api/index.js
@@ -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}) => (
-
+
diff --git a/frontend/src/page/admin/auth/authentication.jsx b/frontend/src/page/admin/auth/authentication.jsx
new file mode 100644
index 0000000000..cd2c065949
--- /dev/null
+++ b/frontend/src/page/admin/auth/authentication.jsx
@@ -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:
,
+ },
+ {
+ label: 'Google',
+ component:
,
+ },
+ ];
+
+ return (
+
+
+
+
+ }
+ />
+
+ You are running the open-source version of Unleash. You have to use the Enterprise edition
+ in order configure Single Sign-on.
+ }
+ />
+ 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.
+ }
+ />
+
+
+ );
+}
+
+AdminAuthPage.propTypes = {
+ match: PropTypes.object.isRequired,
+ history: PropTypes.object.isRequired,
+ authenticationType: PropTypes.string,
+};
+
+export default AdminAuthPage;
diff --git a/frontend/src/page/admin/auth/google-auth-container.js b/frontend/src/page/admin/auth/google-auth-container.js
index 6557d9c059..e5cc107d77 100644
--- a/frontend/src/page/admin/auth/google-auth-container.js
+++ b/frontend/src/page/admin/auth/google-auth-container.js
@@ -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),
});
diff --git a/frontend/src/page/admin/auth/google-auth.jsx b/frontend/src/page/admin/auth/google-auth.jsx
index b960ac73fa..8d5c3770ee 100644
--- a/frontend/src/page/admin/auth/google-auth.jsx
+++ b/frontend/src/page/admin/auth/google-auth.jsx
@@ -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
-
+
Please read the{' '}
documentation
{' '}
to learn how to integrate with Google OAuth 2.0.
- Callback URL: https://[unleash.hostname.com]/auth/google/callback
-
+ Callback URL: {unleashUrl}/auth/google/callback
+