Merge branch 'application-mgt-new' into 'application-mgt-new'

Add roles and enrolled devices of users

See merge request entgra/carbon-device-mgt!343
feature/appm-store/pbac
Dharmakeerthi Lasantha 5 years ago
commit 6717723d69

@ -77,7 +77,6 @@ class DeviceTypesTable extends React.Component {
pagination,
});
}
console.log(JSON.parse(res.data.data))
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {

@ -0,0 +1,181 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from "react";
import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd";
import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext";
const {Text} = Typography;
let config = null;
let apiUrl;
const columns = [
{
title: 'Policy Name',
dataIndex: 'policyName',
width: 100,
},
{
title: 'Description',
dataIndex: 'description',
key: 'description',
// render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Compilance',
dataIndex: 'compliance',
key: 'compliance',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Policy Type',
dataIndex: 'policyType',
key: 'policyType',
// render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
}
];
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class PoliciesTable extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: []
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
}
};
componentDidMount() {
this.fetchGroups();
}
//fetch data from api
fetchGroups = (params = {}) => {
const config = this.props.context;
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), //calculate the offset
limit: 10,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key]).join('&');
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/policies?" + encodedExtraParams;
//send request to the invokerss
axios.get(apiUrl).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.policies,
pagination,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load policies.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record => (record.id)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
scroll={{x: 1000}}
/>
</div>
);
}
}
export default withConfigContext(PoliciesTable);

@ -0,0 +1,250 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import React from "react";
import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider} from "antd";
import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext";
const {Text} = Typography;
let config = null;
let apiUrl;
const columns = [
{
title: 'Device',
dataIndex: 'name',
width: 100,
},
{
title: 'Type',
dataIndex: 'type',
key: 'type',
render: type => {
const defaultPlatformIcons = config.defaultPlatformIcons;
let icon = defaultPlatformIcons.default.icon;
let color = defaultPlatformIcons.default.color;
let theme = defaultPlatformIcons.default.theme;
if (defaultPlatformIcons.hasOwnProperty(type)) {
icon = defaultPlatformIcons[type].icon;
color = defaultPlatformIcons[type].color;
theme = defaultPlatformIcons[type].theme;
}
return (
<span style={{fontSize: 20, color: color, textAlign: "center"}}>
<Icon type={icon} theme={theme}/>
</span>
);
}
// todo add filtering options
},
{
title: 'Owner',
dataIndex: 'enrolmentInfo',
key: 'owner',
render: enrolmentInfo => enrolmentInfo.owner
// todo add filtering options
},
{
title: 'Ownership',
dataIndex: 'enrolmentInfo',
key: 'ownership',
width: 100,
render: enrolmentInfo => enrolmentInfo.ownership
// todo add filtering options
},
{
title: 'Status',
dataIndex: 'enrolmentInfo',
width: 100,
key: 'status',
render: (enrolmentInfo) => {
const status = enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "removed":
color = "#ff7979";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>;
}
// todo add filtering options
},
{
title: 'Last Updated',
dataIndex: 'enrolmentInfo',
key: 'dateOfLastUpdate',
render: (data) => {
const {dateOfLastUpdate} = data;
const timeAgoString = getTimeAgo(dateOfLastUpdate);
return <Tooltip title={new Date(dateOfLastUpdate).toString()}>{timeAgoString}</Tooltip>;
}
// todo add filtering options
}
];
const getTimeAgo = (time) => {
const timeAgo = new TimeAgo('en-US');
return timeAgo.format(time);
};
class UsersDevices extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: []
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
}
};
componentDidMount() {
this.fetch();
}
//Rerender component when parameters change
componentDidUpdate(prevProps, prevState, snapshot) {
if(prevProps.user !== this.props.user){
this.fetch();
}
}
//fetch data from api
fetch = (params = {}) => {
const config = this.props.context;
this.setState({loading: true});
// get current page
const currentPage = (params.hasOwnProperty("page")) ? params.page : 1;
const extraParams = {
offset: 10 * (currentPage - 1), //calculate the offset
limit: 10,
user: this.props.user,
requireDeviceInfo: true,
};
const encodedExtraParams = Object.keys(extraParams)
.map(key => key + '=' + extraParams[key]).join('&');
//send request to the invoker
axios.get(
window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/devices?" + encodedExtraParams,
).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: res.data.data.devices,
pagination
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
this.setState({
pagination: pager,
});
this.fetch({
results: pagination.pageSize,
page: pagination.current,
sortField: sorter.field,
sortOrder: sorter.order,
...filters,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
return (
<div>
<Table
columns={columns}
rowKey={record => (record.deviceIdentifier + record.enrolmentInfo.owner + record.enrolmentInfo.ownership)}
dataSource={data}
showHeader={false}
size="small"
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} devices`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
scroll={{x: 1000}}
/>
</div>
);
}
}
export default withConfigContext(UsersDevices);

@ -18,12 +18,14 @@
import React from "react";
import axios from "axios";
import {Tag, message, notification, Table, Typography, Tooltip, Icon, Divider, Card, Col, Row, Select} from "antd";
import {message, notification, Table, Typography, Panel, Collapse, Button, List, Modal, Icon} from "antd";
import TimeAgo from 'javascript-time-ago'
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'
import {withConfigContext} from "../../context/ConfigContext";
import DeviceTable from "../Devices/DevicesTable";
import UsersDevices from "./UsersDevices";
const {Text} = Typography;
@ -39,10 +41,21 @@ class UsersTable extends React.Component {
data: [],
pagination: {},
loading: false,
selectedRows: []
selectedRows: [],
rolesModalVisible: false,
rolesData: [],
user:''
};
}
rowSelection = {
onChange: (selectedRowKeys, selectedRows) => {
this.setState({
selectedRows: selectedRows
})
}
};
componentDidMount() {
this.fetchUsers();
}
@ -65,7 +78,7 @@ class UsersTable extends React.Component {
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/users";
"/users?" + encodedExtraParams;
//send request to the invokerss
axios.get(apiUrl).then(res => {
@ -95,6 +108,44 @@ class UsersTable extends React.Component {
});
};
//fetch data from api
fetchRoles = (username) => {
const config = this.props.context;
this.setState({
rolesModalVisible: true,
user: username
});
apiUrl = window.location.origin + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt +
"/users/" + username + "/roles";
//send request to the invokerss
axios.get(apiUrl).then(res => {
if (res.status === 200) {
this.setState({
rolesData: res.data.data.roles,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
//todo display a popop with error
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:"Error occurred while trying to load roles.",
});
}
this.setState({loading: false});
});
};
handleTableChange = (pagination, filters, sorter) => {
const pager = {...this.state.pagination};
pager.current = pagination.current;
@ -110,22 +161,102 @@ class UsersTable extends React.Component {
});
};
handleOk = e => {
this.setState({
rolesModalVisible: false,
});
};
handleCancel = e => {
this.setState({
rolesModalVisible: false,
});
};
render() {
const {data, pagination, loading, selectedRows} = this.state;
const itemCard = data.map((data) =>
<Col span={8} key={data.username}>
<Card hoverable title="User Name" bordered={true}>
{data.username}
</Card>
</Col>
);
const { Panel } = Collapse;
const columns = [
{
title: 'User Name',
dataIndex: 'username',
width: 150,
key: "username",
},
{
title: 'First Name',
width: 150,
dataIndex: 'firstname',
key: 'firstname',
},
{
title: 'Last Name',
width: 150,
dataIndex: 'lastname',
key: 'lastname',
},
{
title: 'Email',
width: 100,
dataIndex: 'emailAddress',
key: 'emailAddress',
},
{
title: '',
dataIndex: 'username',
key: 'roles',
render: (username) =>
<Button
type="link"
size={"default"}
icon="info-circle"
onClick={() => this.fetchRoles(username)}>Info</Button>
}
];
return (
<div style={{ background: '#ECECEC', padding: '30px' }}>
<Row gutter={16}>
{itemCard}
</Row>
<div>
<div>
<Table
columns={columns}
rowKey={record => (record.username)}
dataSource={data}
pagination={{
...pagination,
size: "small",
// position: "top",
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true
}}
loading={loading}
onChange={this.handleTableChange}
rowSelection={this.rowSelection}
scroll={{x: 1000}}
/>
</div>
<div>
<Modal
width="900px"
title="Info"
visible={this.state.rolesModalVisible}
onOk={this.handleOk}
onCancel={this.handleCancel}
>
<Collapse>
<Panel header="User Roles" key="1">
<List
size="small"
bordered
dataSource={this.state.rolesData}
renderItem={item => <List.Item>{item}</List.Item>}
/>
</Panel>
<Panel header="Enrolled Devices" key="2">
<UsersDevices user={this.state.user}/>
</Panel>
</Collapse>
</Modal>
</div>
</div>
);
}

@ -54,6 +54,9 @@ class Groups extends React.Component {
<GroupsTable/>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
}

@ -24,6 +24,7 @@ import {
Icon
} from "antd";
import {Link} from "react-router-dom";
import PoliciesTable from "../../../components/Policies/PoliciesTable";
const {Paragraph} = Typography;
@ -50,9 +51,12 @@ class Policies extends React.Component {
<Paragraph>All policies for device management</Paragraph>
</div>
<div style={{backgroundColor:"#ffffff", borderRadius: 5}}>
<PoliciesTable/>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
</div>
</div>
);
}

Loading…
Cancel
Save