Merge branch 'master' into 'master'

Generate UI component for profile configuration in Add Policy view using json

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

@ -119,7 +119,7 @@ class GroupsTable extends React.Component {
const pagination = {...this.state.pagination}; const pagination = {...this.state.pagination};
this.setState({ this.setState({
loading: false, loading: false,
data: res.data.data.deviceGroups, data: res.data.data,
pagination, pagination,
}); });
} }
@ -149,7 +149,7 @@ class GroupsTable extends React.Component {
this.setState({ this.setState({
pagination: pager, pagination: pager,
}); });
this.fetch({ this.fetchGroups({
results: pagination.pageSize, results: pagination.pageSize,
page: pagination.current, page: pagination.current,
sortField: sorter.field, sortField: sorter.field,
@ -171,11 +171,13 @@ class GroupsTable extends React.Component {
<Table <Table
columns={this.columns} columns={this.columns}
rowKey={record => (record.id)} rowKey={record => (record.id)}
dataSource={data} dataSource={data.deviceGroups}
pagination={{ pagination={{
...pagination, ...pagination,
size: "small", size: "small",
// position: "top", // position: "top",
total: data.count,
pageSize: 2,
showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups` showTotal: (total, range) => `showing ${range[0]}-${range[1]} of ${total} groups`
// showQuickJumper: true // showQuickJumper: true
}} }}

@ -3,6 +3,7 @@ import {Button, Form, Row, Col, Card, Steps, Input, message, Modal, notification
import axios from "axios"; import axios from "axios";
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
import DeviceType from "../Devices/DeviceType"; import DeviceType from "../Devices/DeviceType";
import SelectPlatform from "./SelectPlatform";
import ConfigureProfile from "./ConfigureProfile"; import ConfigureProfile from "./ConfigureProfile";
const {Step} = Steps; const {Step} = Steps;
@ -23,6 +24,16 @@ class AddPolicy extends React.Component {
}) })
}; };
next() {
const current = this.state.current + 1;
this.setState({ current });
}
prev() {
const current = this.state.current - 1;
this.setState({ current });
}
openAddDeviceModal = () =>{ openAddDeviceModal = () =>{
this.setState({ this.setState({
isAddDeviceModalVisible : true, isAddDeviceModalVisible : true,
@ -46,10 +57,9 @@ class AddPolicy extends React.Component {
</Steps> </Steps>
</Col> </Col>
<Col span={16} offset={4}> <Col span={16} offset={4}>
<Card style={{marginTop: 24}}> <Card style={{marginTop: 24}}>
<div style={{display: (current === 0 ? 'unset' : 'none')}}> <div style={{display: (current === 0 ? 'unset' : 'none')}}>
<DeviceType onClickType={this.onClickType}/> <SelectPlatform onClickType={this.onClickType}/>
</div> </div>
<div style={{display: (current === 1 ? 'unset' : 'none')}}> <div style={{display: (current === 1 ? 'unset' : 'none')}}>
<ConfigureProfile/> <ConfigureProfile/>
@ -64,8 +74,28 @@ class AddPolicy extends React.Component {
</div> </div>
</Card> </Card>
</Col> </Col>
<Col span={16} offset={4}>
<div style={{marginTop: 24}}>
{current > 0 && (
<Button style={{ marginRight: 8 }} onClick={() => this.prev()}>
Previous
</Button>
)}
{current < 5 && current > 0 && (
<Button type="primary" onClick={() => this.next()}>
Next
</Button>
)}
{current === 5 && (
<Button type="primary">
Done
</Button>
)}
</div>
</Col>
</Row> </Row>
</div> </div>
); );
} }

@ -1,302 +1,268 @@
import React from 'react'; import React from 'react';
import {Tabs, Row, Col, Switch, Menu,Input, Typography, Form, Checkbox, Select} from "antd"; import {Tabs, Row, Col, Switch, Menu,Input, Typography, Form, Checkbox, Select,
Tooltip, Icon, Collapse, Alert, Upload, Button,Radio} from "antd";
import {withConfigContext} from "../../context/ConfigContext"; import {withConfigContext} from "../../context/ConfigContext";
const { Title } = Typography; import "../../pages/Dashboard/Policies/policies.css";
import jsonResponse from "./configuration";
const { Title, Text, Paragraph } = Typography;
const { TabPane } = Tabs; const { TabPane } = Tabs;
const {Option} = Select; const {Option} = Select;
const {Panel} = Collapse;
const { TextArea } = Input;
const policyConfigurationsList = jsonResponse.PolicyConfigurations;
class ConfigureProfile extends React.Component { class ConfigureProfile extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.config = this.props.context; this.config = this.props.context;
this.policies = policyConfigurationsList.androidPolicy.Policy;
this.state = {
isDisplayMain: "none",
activeKeys: []
}
}; };
render() { componentDidMount() {
}
onChange = (e) =>{
console.log(`checked = ${e.target.id}`);
};
onChecked = (e,i) =>{
if(e){
this.setState({
isDisplayMain: "block",
});
}else{
this.setState({
isDisplayMain: "none",
});
}
};
onClickSwitch = (e) =>{
};
getPanelItems = (panel)=>{
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
return ( return (
<div> panel.map((item,k)=>{
<Tabs tabPosition={"left"} type={"card"} size={"large"}> switch(item._type){
<TabPane tab="Passcode Policy" key="1"> case "select":
<Row> return(
<Col offset={0} span={8}> <Form.Item key={k}
<Title level={4}>Passcode Policy</Title> label={
</Col> <span>
<Col offset={12} span={2} > {item.Label}&nbsp;
<Switch checkedChildren=" ON " unCheckedChildren="OFF"/> <Tooltip title={item.tooltip} placement="right">
</Col> <Icon type="question-circle-o" />
</Row> </Tooltip>
<Row> </span>
Enforce a configured passcode policy on Android devices. }
Once this profile is applied, the device style={{display: "block"}}>
owners won't be able to modify the password settings on their devices. {getFieldDecorator(`${item._id}`, {
</Row> initialValue: `${item.Optional.Option[0]}`
<div>
<Form>
<Form.Item >
{getFieldDecorator('allowSimpleValue', {
})(<Checkbox>Allow simple value</Checkbox>)}
</Form.Item>
<Form.Item >
{getFieldDecorator('remember', {
})(<Checkbox>Require alphanumeric value</Checkbox>)}
</Form.Item>
<Form.Item label="Minimum passcode length" style={{display: "block"}}>
{getFieldDecorator('minPasscodeLength', {
initialValue: '0'
})( })(
<Select> <Select>
<Option key="0">None</Option> {item.Optional.Option.map((option)=>{
<Option key="4">4</Option> return(
<Option key="5">5</Option> <Option key={option}>{option}</Option>
<Option key="6">6</Option> );
<Option key="7">7</Option> })}
<Option key="8">8</Option>
<Option key="9">9</Option>
<Option key="10">10</Option>
<Option key="11">11</Option>
<Option key="12">12</Option>
<Option key="13">13</Option>
<Option key="14">14</Option>
<Option key="15">15</Option>
</Select> </Select>
)} )}
</Form.Item> </Form.Item>
<Form.Item label="Minimum number of complex characters" style={{display: "block"}}> );
{getFieldDecorator('minComplexChar', { case "input":
initialValue: '0' return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
rules: [
{
pattern: new RegExp(`${item.Optional.rules.regex}`),
message: `${item.Optional.rules.validationMsg}`,
},
],
})( })(
<Select> <Input placeholder={item.Optional.Placeholder}/>
<Option key="0">None</Option> )}
<Option key="1">1</Option> </Form.Item>
<Option key="2">2</Option> );
<Option key="3">3</Option> case "checkbox":
<Option key="4">4</Option> return(
<Option key="5">5</Option> <Form.Item key={k}>
</Select> {getFieldDecorator(`${item._id}`, {
valuePropName: 'checked',
initialValue: `${item.Optional.checked}`,
})(
<Checkbox
// checked={item.Optional.checked}
onChange={this.onChange}
>
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>
)} )}
</Form.Item> </Form.Item>
<Form.Item label="Maximum passcode age in days* " style={{display: "block"}}> );
{getFieldDecorator('maxPasscodeAge', { case "textArea":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
})( })(
<Input/> <TextArea placeholder={item.Optional.Placeholder}
rows={item.Optional.Row} />
)} )}
</Form.Item> </Form.Item>
<Form.Item label="Passcode history*" style={{display: "block"}}> );
{getFieldDecorator('passcodeHistory', { case "radioGroup":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
style={{display: "block"}}>
{getFieldDecorator(`${item._id}`, {
})( })(
<Input/> <Radio.Group>
{item.Optional.Radio.map((option)=>{
return(
<Radio value={option}>{option}</Radio>
);
})}
</Radio.Group>
)} )}
</Form.Item> </Form.Item>
<Form.Item label="Maximum number of failed attempts" style={{display: "block"}}> );
{getFieldDecorator('maxFailedAttemps', { case "title":
initialValue: '0' return(
<Title key={k} level={4}>{item.Label} </Title>
);
case "paragraph":
return(
<Paragraph key={k} style={{marginTop:20}}>{item.Label} </Paragraph>
);
case "alert":
return(
<Alert
key={k}
description={item.Label}
type="warning"
showIcon
/>
);
case "upload":
return(
<Form.Item key={k}
label={
<span>
{item.Label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('upload', {
})( })(
<Select>
<Option key="0">None</Option> <Upload>
<Option key="3">3</Option> <Button>
<Option key="4">4</Option> <Icon type="upload" /> Click to upload
<Option key="5">5</Option> </Button>
<Option key="6">6</Option> </Upload>,
<Option key="7">7</Option>
<Option key="8">8</Option>
<Option key="9">9</Option>
<Option key="10">10</Option>
</Select>
)} )}
</Form.Item> </Form.Item>
</Form> );
</div> default:
</TabPane> return null;
<TabPane tab="Restrictions" key="2"> }
<Row> })
<Col offset={0} span={8}> )
<Title level={4}>Restrictions</Title> };
</Col>
<Col offset={12} span={2} > render() {
<Switch checkedChildren=" ON " unCheckedChildren="OFF" /> return (
</Col>
</Row>
<Row>
<p>This configurations can be used to restrict certain settings on an Android device.
Once this configuration profile is installed on a device, corresponding users will
not be able to modify these settings on their devices.</p>
</Row>
</TabPane>
<TabPane tab="Encryption Settings" key="3">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Encryption Settings</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configuration can be used to encrypt data on an Android device,
when the device is locked and make it readable when the passcode is entered.
Once this configuration profile is installed on a device, corresponding users will
not be able to modify these settings on their devices.</p>
</Row>
</TabPane>
<TabPane tab="Wi-Fi Settings" key="4">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Wi-Fi Settings</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF"/>
</Col>
</Row>
<Row>
<p>This configurations can be used to configure Wi-Fi access on an Android device.
Once this configuration profile is installed on a device, corresponding
users will not be able to modify these settings on their devices.</p>
</Row>
</TabPane>
<TabPane tab="Global Proxy Settings" key="5">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Global Proxy Settings</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configurations can be used to set a network-independent global HTTP
proxy on an Android device. Once this configuration profile is installed on a device,
all the network traffic will be routed through the proxy server.</p>
</Row>
</TabPane>
<TabPane tab="Virtual Private Network" key="6">
<div> <div>
<Row> <Tabs tabPosition={"left"} size={"large"}>
<Col offset={0} span={8}> { this.policies.map((element, i) =>{
<Title level={4}>VPN Settings</Title> return(
</Col>
<Col offset={12} span={2} > <TabPane tab={element.Name} key={i} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" /> {/*<div style={{ height: 800, overflowY: "scroll"}}>*/}
</Col> { Object.values(element.Panel).map((panel, j)=>{
</Row> return(
<Row> <div key={j} >
<p>Configure the OpenVPN settings on Android devices. In order to enable this,
device needs to have "OpenVPN for Android" application installed.</p>
</Row>
</div>
<div> <div>
<Row> <Row>
<Col offset={0} span={8}> <Col offset={0} span={14}>
<Title level={4}>Always On VPN Settings</Title> <Title level={4}>{panel.title} </Title>
</Col> </Col>
<Col offset={12} span={2} > <Col offset={8} span={1}>
<Switch checkedChildren=" ON " unCheckedChildren="OFF" /> <Switch
checkedChildren="ON"
unCheckedChildren="OFF"
id={i}
onClick={this.onClickSwitch}
onChange={this.onChecked}
/>
</Col> </Col>
</Row> </Row>
<Row> <Row>{panel.description}</Row>
<p>Configure an always-on VPN connection through a specific VPN client application.</p>
</Row>
</div> </div>
<div style={{display: `${this.state.isDisplayMain}`}}>
<Form >
{this.getPanelItems(panel.PanelItem)}
</Form>
</div>
</div>
);
})
}
</TabPane> </TabPane>
<TabPane tab="Certificate Install" key="7"> )
<Row> })
<Col offset={0} span={8}> }
<Title level={4}>Certificate Install Settings</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>Configure the certificate install settings on Android devices.</p>
</Row>
</TabPane>
<TabPane tab="Work-Profile Configurations" key="8">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Work-Profile Configurations</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>The configurations below can be applied to the devices where the agent
is running in Android Work-Profile.</p>
</Row>
</TabPane>
<TabPane tab="COSU Profile Configuration" key="9">
<Row>
<Col offset={0} span={8}>
<Title level={4}>COSU Profile Configuration</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This policy can be used to configure the profile of COSU Devices.</p>
</Row>
</TabPane>
<TabPane tab="Application Restrictions" key="10">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Application Restrictions</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configuration can be used to create a black list or white list of applications.</p>
</Row>
</TabPane>
<TabPane tab="Runtime Permission Policy" key="11">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Runtime Permission Policy</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configuration can be used to set a runtime permission policy to an Android Device.</p>
</Row>
</TabPane>
<TabPane tab="System Update Policy (COSU)" key="12">
<Row>
<Col offset={0} span={8}>
<Title level={4}>System Update Policy (COSU)</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configuration can be used to set a passcode policy to an Android Device.
Once this configuration profile is installed on a device,
corresponding users will not be able to modify these settings on their devices.</p>
</Row>
</TabPane>
<TabPane tab="Enrollment Application Install" key="13">
<Row>
<Col offset={0} span={8}>
<Title level={4}>Enrollment Application Install</Title>
</Col>
<Col offset={12} span={2} >
<Switch checkedChildren=" ON " unCheckedChildren="OFF" />
</Col>
</Row>
<Row>
<p>This configuration can be used to install applications during Android device enrollment.</p>
</Row>
</TabPane>
</Tabs> </Tabs>
</div> </div>
); );
} }
} }
export default withConfigContext(Form.create({name: 'add-policy'})(ConfigureProfile)); export default withConfigContext(Form.create()(ConfigureProfile));

@ -0,0 +1,148 @@
/*
* 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 {Card, Col, Icon, message, notification, Row, Typography} 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;
class SelectPlatform extends React.Component {
constructor(props) {
super(props);
config = this.props.context;
TimeAgo.addLocale(en);
this.state = {
data: [],
pagination: {},
loading: false,
selectedRows: []
};
}
componentDidMount() {
this.fetchUsers();
}
onClickCard = (data) =>{
console.log(data);
this.props.onClickType();
};
//fetch data from api
fetchUsers = (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 +
"/device-types";
//send request to the invokerss
axios.get(apiUrl).then(res => {
if (res.status === 200) {
const pagination = {...this.state.pagination};
this.setState({
loading: false,
data: JSON.parse(res.data.data),
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 device types.",
});
}
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;
const { Meta } = Card;
const itemCard = data.map((data) =>
<Col span={5} key={data.id}>
<Card
size="default"
style={{ width: 150 }}
bordered={true}
onClick={this.onClickCard}
cover={<Icon type="android" key="device-types" style={{color:'#ffffff',
backgroundColor:'#4b92db', fontSize: '100px', padding:'20px'}}/>}
>
<Meta
title={data.name}
/>
</Card>
</Col>
);
return (
<div>
<Row gutter={16}>
{itemCard}
</Row>
</div>
);
}
}
export default withConfigContext(SelectPlatform);

@ -0,0 +1,29 @@
.ant-tabs-content {
/*height: 120px;*/
margin-top: -16px;
}
.ant-tabs-content > .ant-tabs-tabpane {
/*background: #000066;*/
/*color: #ffffff;*/
/*padding: 16px;*/
}
.ant-tabs-bar {
border-color: #3498db;
}
.ant-tabs-bar .ant-tabs-tab {
border-color: #3498db;
background: transparent;
/*color: #cc0000;*/
}
.ant-tabs-bar .ant-tabs-tab-active {
/*border-color: #1b3bcc;*/
/*background: #3498db;*/
/*color: #cc0000;*/
}
Loading…
Cancel
Save