Merge branch 'master' of https://gitlab.com/entgra/carbon-device-mgt into entgra-master

feature/appm-store/pbac
Charitha Goonetilleke 5 years ago
commit 07ce838251

@ -42,6 +42,8 @@ import Roles from './scenes/Home/scenes/Roles';
import DeviceTypes from './scenes/Home/scenes/DeviceTypes'; import DeviceTypes from './scenes/Home/scenes/DeviceTypes';
import Certificates from './scenes/Home/scenes/Configurations/scenes/Certificates'; import Certificates from './scenes/Home/scenes/Configurations/scenes/Certificates';
import Devices from './scenes/Home/scenes/Devices'; import Devices from './scenes/Home/scenes/Devices';
import ViewPolicy from './scenes/Home/scenes/Policies/scenes/ViewPolicy';
import EditSelectedPolicy from './scenes/Home/scenes/Policies/scenes/EditSelectedPolicy';
const routes = [ const routes = [
{ {
@ -94,6 +96,16 @@ const routes = [
component: AddNewPolicy, component: AddNewPolicy,
exact: true, exact: true,
}, },
{
path: '/entgra/policy/view/:policyId',
component: ViewPolicy,
exact: true,
},
{
path: '/entgra/policy/edit/:policyId',
component: EditSelectedPolicy,
exact: true,
},
{ {
path: '/entgra/roles', path: '/entgra/roles',
component: Roles, component: Roles,

@ -52,12 +52,17 @@ class AssignGroups extends React.Component {
onHandleContinue = (e, formName) => { onHandleContinue = (e, formName) => {
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
if (typeof values.roles === 'string') {
values.roles = [values.roles];
}
if (!values.users) { if (!values.users) {
delete values.users; delete values.users;
} }
if (values.deviceGroups === 'NONE') { if (values.deviceGroups === 'NONE') {
delete values.deviceGroups; delete values.deviceGroups;
} }
this.props.getPolicyPayloadData(formName, values); this.props.getPolicyPayloadData(formName, values);
this.props.getNextStep(); this.props.getNextStep();
} }

@ -45,6 +45,8 @@ const { TabPane } = Tabs;
const { Option } = Select; const { Option } = Select;
const { TextArea } = Input; const { TextArea } = Input;
const subPanelpayloadAttributes = {};
class ConfigureProfile extends React.Component { class ConfigureProfile extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
@ -54,6 +56,8 @@ class ConfigureProfile extends React.Component {
isDisplayMain: 'none', isDisplayMain: 'none',
activePanelKeys: [], activePanelKeys: [],
activeSubPanelKeys: [], activeSubPanelKeys: [],
subFormList: [],
subPanelpayloadAttributes: {},
count: 0, count: 0,
dataArray: [], dataArray: [],
customInputDataArray: [], customInputDataArray: [],
@ -297,17 +301,64 @@ class ConfigureProfile extends React.Component {
// generate payload by adding policy configurations // generate payload by adding policy configurations
onHandleContinue = (e, formname) => { onHandleContinue = (e, formname) => {
this.props.form.validateFields((err, values) => { const allFields = this.props.form.getFieldsValue();
let activeFields = [];
// get currently active field list
for (let i = 0; i < this.state.activePanelKeys.length; i++) {
Object.keys(allFields).map(key => {
if (key.includes(`${this.state.activePanelKeys[i]}-`)) {
if (
subPanelpayloadAttributes.hasOwnProperty(
`${this.state.activePanelKeys[i]}`,
)
) {
Object.keys(
subPanelpayloadAttributes[this.state.activePanelKeys[i]],
).map(subPanel => {
if (`${this.state.activePanelKeys[i]}-${subPanel}` === true) {
if (key.includes(`-${subPanel}-`)) {
activeFields.push(key);
}
} else if (!key.includes(`-${subPanel}-`)) {
activeFields.push(key);
}
});
} else {
activeFields.push(key);
}
}
});
}
// validate fields and get profile features list
this.props.form.validateFields(activeFields, (err, values) => {
if (!err) { if (!err) {
this.props.getPolicyPayloadData(formname, values); let profileFeaturesList = [];
for (let i = 0; i < this.state.activePanelKeys.length; i++) {
let content = {};
Object.entries(values).map(([key, value]) => {
if (key.includes(`${this.state.activePanelKeys[i]}-`)) {
content[
key.replace(`${this.state.activePanelKeys[i]}-`, '')
] = value;
}
});
let feature = {
featureCode: this.state.activePanelKeys[i],
deviceType: 'android',
content: content,
};
profileFeaturesList.push(feature);
}
this.props.getPolicyPayloadData(formname, profileFeaturesList);
this.props.getNextStep(); this.props.getNextStep();
} }
}); });
}; };
// generate form items // generate form items
getPanelItems = panel => { getPanelItems = (panel, panelId) => {
const { getFieldDecorator } = this.props.form; const { getFieldDecorator } = this.props.form;
const subPanelList = {};
return panel.map((item, k) => { return panel.map((item, k) => {
switch (item.type) { switch (item.type) {
case 'select': case 'select':
@ -407,7 +458,6 @@ class ConfigureProfile extends React.Component {
style={{ display: 'block' }} style={{ display: 'block' }}
> >
{getFieldDecorator(`${item.id}`, { {getFieldDecorator(`${item.id}`, {
// valuePropName: 'option',
initialValue: item.optional.initialDataIndex, initialValue: item.optional.initialDataIndex,
})( })(
<Select> <Select>
@ -477,9 +527,21 @@ class ConfigureProfile extends React.Component {
<div> <div>
<div> <div>
{item.optional.subPanel.map((panel, i) => { {item.optional.subPanel.map((panel, i) => {
subPanelList[panel.others.itemSwitch] =
panel.others.itemPayload;
if (
subPanelpayloadAttributes.hasOwnProperty(panelId)
) {
Object.assign(
subPanelpayloadAttributes[panelId],
subPanelList,
);
} else {
subPanelpayloadAttributes[panelId] = subPanelList;
}
return ( return (
<div key={i}> <div key={i}>
{this.getPanelItems(panel.panelItem)} {this.getPanelItems(panel.panelItem, panelId)}
</div> </div>
); );
})} })}
@ -507,7 +569,6 @@ class ConfigureProfile extends React.Component {
)} )}
</Form.Item> </Form.Item>
); );
case 'textArea': case 'textArea':
return ( return (
<Form.Item <Form.Item
@ -522,7 +583,9 @@ class ConfigureProfile extends React.Component {
} }
style={{ display: 'block' }} style={{ display: 'block' }}
> >
{getFieldDecorator(`${item.id}`, {})( {getFieldDecorator(`${item.id}`, {
initialValue: null,
})(
<TextArea <TextArea
placeholder={item.optional.placeholder} placeholder={item.optional.placeholder}
rows={item.optional.row} rows={item.optional.row}
@ -679,6 +742,7 @@ class ConfigureProfile extends React.Component {
render() { render() {
const { policyUIConfigurationsList } = this.props; const { policyUIConfigurationsList } = this.props;
const { getFieldDecorator } = this.props.form;
return ( return (
<div className="tab-container"> <div className="tab-container">
<Tabs tabPosition={'left'} size={'large'}> <Tabs tabPosition={'left'} size={'large'}>
@ -722,7 +786,17 @@ class ConfigureProfile extends React.Component {
> >
{panel.hasOwnProperty('panelItem') && ( {panel.hasOwnProperty('panelItem') && (
<div> <div>
<Form>{this.getPanelItems(panel.panelItem)}</Form> <Form name={panel.panelId}>
<Form.Item style={{ display: 'none' }}>
{getFieldDecorator(`${panel.panelId}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(
panel.panelItem,
panel.panelId,
)}
</Form>
</div> </div>
)} )}
{panel.hasOwnProperty('subFormLists') && ( {panel.hasOwnProperty('subFormLists') && (
@ -730,7 +804,12 @@ class ConfigureProfile extends React.Component {
{Object.values(panel.subFormLists).map( {Object.values(panel.subFormLists).map(
(form, i) => { (form, i) => {
return ( return (
<Form key={i}> <Form name={form.id} key={i}>
<Form.Item style={{ display: 'none' }}>
{getFieldDecorator(`${form.id}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(form.panelItem)} {this.getPanelItems(form.panelItem)}
</Form> </Form>
); );

@ -29,9 +29,7 @@ class PublishDevices extends React.Component {
onClickSavePolicy = (event, isPublish, formName) => { onClickSavePolicy = (event, isPublish, formName) => {
this.props.form.validateFields((err, values) => { this.props.form.validateFields((err, values) => {
if (!err) { if (!err) {
if (isPublish) { values.active = isPublish;
values = { ...values, ...{ active: isPublish } };
}
this.props.getPolicyPayloadData(formName, values); this.props.getPolicyPayloadData(formName, values);
} }
}); });

@ -96,7 +96,7 @@ class SelectPlatform extends React.Component {
} }
cover={ cover={
<Icon <Icon
type="android" type={data.name === 'ios' ? 'apple' : data.name}
key="device-types" key="device-types"
style={{ style={{
color: '#ffffff', color: '#ffffff',

@ -52,28 +52,30 @@ class AddPolicy extends React.Component {
}; };
createPayload = () => { createPayload = () => {
const { newPolicyPayload } = this.state;
const { const {
publishDevicesData, publishDevicesData,
selectedPlatformData, selectedPlatformData,
policyProfile, configureProfileData,
policyTypeData, policyTypeData,
groupData, groupData,
} = this.state.payloadData; } = this.state.payloadData;
let profile = { const profile = {
policyName: publishDevicesData.policyName, profileName: publishDevicesData.policyName,
devicetype: selectedPlatformData.deviceType, deviceType: selectedPlatformData.deviceType,
profileFeaturesList: configureProfileData,
}; };
let payload = { const payload = {
...newPolicyPayload, policyName: publishDevicesData.policyName,
...publishDevicesData, description: publishDevicesData.description,
...policyProfile, compliance: 'enforce',
ownershipType: null,
active: publishDevicesData.active,
...policyTypeData, ...policyTypeData,
profile: profile,
...groupData, ...groupData,
...{ profile: profile },
}; };
console.log(payload); this.onAddNewPolicy(JSON.stringify(payload));
}; };
getPolicyConfigJson = type => { getPolicyConfigJson = type => {
@ -114,6 +116,40 @@ class AddPolicy extends React.Component {
}); });
}; };
onAddNewPolicy = value => {
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/',
value,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 201) {
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully added new Policy.',
});
}
})
.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 add New Policy.',
});
}
});
};
getNextStep = () => { getNextStep = () => {
const currentStepIndex = this.state.currentStepIndex + 1; const currentStepIndex = this.state.currentStepIndex + 1;
this.setState({ currentStepIndex }); this.setState({ currentStepIndex });

@ -0,0 +1,269 @@
/*
* Copyright (c) 2020, 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 { withConfigContext } from '../../../../../../../../components/ConfigContext';
import { Button, Col, Form, message, notification, Radio, Select } from 'antd';
import axios from 'axios';
const { Option } = Select;
class AssignGroups extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.userSelector = React.createRef();
this.roleSelector = React.createRef();
this.state = {
roles: [],
users: [],
groups: [],
};
}
componentDidMount() {
this.getRolesList();
this.getGroupsList();
}
handleSetUserRoleFormItem = event => {
if (event.target.value === 'roleSelector') {
this.roleSelector.current.style.cssText = 'display: block;';
this.userSelector.current.style.cssText = 'display: none;';
} else {
this.roleSelector.current.style.cssText = 'display: none;';
this.userSelector.current.style.cssText = 'display: block;';
}
};
// generate payload by adding Assign Groups
onHandleContinue = (e, formName) => {
this.props.form.validateFields((err, values) => {
if (!err) {
if (typeof values.roles === 'string') {
values.roles = [values.roles];
}
if (!values.users) {
delete values.users;
}
if (values.deviceGroups === 'NONE') {
delete values.deviceGroups;
}
this.props.getPolicyPayloadData(formName, values);
this.props.getNextStep();
}
});
};
getRolesList = () => {
let apiURL =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/roles?user-store=PRIMARY&limit=100';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
this.setState({
roles: 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.',
});
}
});
};
getUsersList = value => {
let apiURL =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/users/search/usernames?filter=' +
value +
'&domain=Primary';
axios
.get(apiURL)
.then(res => {
if (res.status === 200) {
let users = JSON.parse(res.data.data);
this.setState({
users,
});
}
})
.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 users.',
});
}
});
};
// fetch data from api
getGroupsList = () => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/admin/groups';
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
groups: res.data.data.deviceGroups,
});
}
})
.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 groups.',
});
}
});
};
render() {
const { policyData } = this.props;
const { getFieldDecorator } = this.props.form;
let deviceGroups = null;
if (policyData.deviceGroups.length > 0) {
deviceGroups = policyData.deviceGroups;
} else {
deviceGroups = 'NONE';
}
return (
<div>
<div>
<Radio.Group
defaultValue={'roleSelector'}
onChange={this.handleSetUserRoleFormItem}
>
<Radio value="roleSelector">Set User role(s)</Radio>
<Radio value="userSelector">Set User(s)</Radio>
</Radio.Group>
<div
id={'roleSelector'}
ref={this.roleSelector}
style={{ display: 'block' }}
>
<Form.Item>
{getFieldDecorator('roles', {
initialValue: policyData.roles,
})(
<Select
mode="multiple"
style={{ width: '100%' }}
defaultActiveFirstOption={true}
>
<Option value={'ANY'}>Any</Option>
{this.state.roles.map(role => (
<Option key={role} value={role}>
{role}
</Option>
))}
</Select>,
)}
</Form.Item>
</div>
<div
id={'userSelector'}
ref={this.userSelector}
style={{ display: 'none' }}
>
<Form.Item>
{getFieldDecorator('users', {
initialValue: policyData.users,
})(
<Select
mode="multiple"
style={{ width: '100%' }}
onSearch={this.getUsersList}
>
{this.state.users.map(user => (
<Option key={user.username} value={user.username}>
{user.username}
</Option>
))}
</Select>,
)}
</Form.Item>
</div>
</div>
<Form.Item label={'Select Groups'} style={{ display: 'block' }}>
{getFieldDecorator('deviceGroups', {
initialValue: deviceGroups,
})(
<Select mode="multiple" style={{ width: '100%' }}>
<Option value={'NONE'}>NONE</Option>
{this.state.groups.map(group => (
<Option key={group.name} value={group.name}>
{group.name}
</Option>
))}
</Select>,
)}
</Form.Item>
<Col span={16} offset={20}>
<div style={{ marginTop: 24 }}>
<Button style={{ marginRight: 8 }} onClick={this.props.getPrevStep}>
Back
</Button>
<Button
type="primary"
onClick={e => this.onHandleContinue(e, 'groupData')}
>
Continue
</Button>
</div>
</Col>
</div>
);
}
}
export default withConfigContext(Form.create()(AssignGroups));

@ -0,0 +1,873 @@
/*
* Copyright (c) 2020, 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 {
Tabs,
Row,
Col,
Switch,
Input,
Typography,
Form,
Collapse,
Checkbox,
Select,
Tooltip,
Icon,
Table,
Alert,
Upload,
Popconfirm,
Button,
Radio,
} from 'antd';
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
import '../../../../styles.css';
import moment from 'moment';
const { Text, Title, Paragraph } = Typography;
const { TabPane } = Tabs;
const { Option } = Select;
const { TextArea } = Input;
const subPanelpayloadAttributes = {};
class ConfigureProfile extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
loading: false,
activePanelKeys: [],
activeSubPanelKeys: [],
subFormList: [],
subPanelpayloadAttributes: {},
customInputDataArray: [],
inputTableDataSources: {},
};
}
setProfileInfo = e => {
let activePolicies = [];
let activePolicyFields = {};
const allFields = this.props.form.getFieldsValue();
this.props.policyFeatureList.map(element => {
activePolicies.push(element.featureCode);
let featureData = JSON.parse(element.content);
Object.keys(featureData).map(key => {
let regex = new RegExp(`${element.featureCode}.+${key}`, 'g');
Object.keys(allFields).map(fieldName => {
if (fieldName.match(regex) != null) {
activePolicyFields[fieldName] = featureData[key];
}
});
});
});
this.props.form.setFieldsValue(activePolicyFields);
this.setState({
activePanelKeys: activePolicies,
});
};
// convert time from 24h format to 12h format
timeConverter = time => {
time = time
.toString()
.match(/^([01]\d|2[0-3])(:)([0-5]\d)(:[0-5]\d)?$/) || [time];
if (time.length > 1) {
time = time.slice(1);
time[5] = +time[0] < 12 ? ' AM' : ' PM';
time[0] = +time[0] % 12 || 12;
}
return time.join('');
};
// get Option value from start Time, end Time and time difference between 2 values
getOptionForTimeSelectors = (startTimeValue, endTimeValue, timeIncrement) => {
let timeOptions = [];
let time = new Date(
moment()
.startOf('day')
.format('YYYY/MM/DD'),
);
let tempValue = startTimeValue;
time.setMinutes(time.getMinutes() + tempValue);
let startOption = (
<Option value={String(tempValue)}>
{this.timeConverter(
`${String(time)
.split(' ')[4]
.substring(0, 5)}`,
)}
</Option>
);
timeOptions.push(startOption);
while (tempValue !== endTimeValue) {
time = new Date(
moment()
.startOf('day')
.format('YYYY/MM/DD'),
);
tempValue += timeIncrement;
if (tempValue > 1440) {
tempValue = 0;
continue;
}
time.setMinutes(time.getMinutes() + tempValue);
let option = (
<Option value={String(tempValue)}>
{this.timeConverter(
`${String(time)
.split(' ')[4]
.substring(0, 5)}`,
)}
</Option>
);
timeOptions.push(option);
}
return timeOptions;
};
// handle items which handle from radio buttons
handleRadioPanel = (e, subPanel) => {
{
subPanel.map((panel, i) => {
if (panel.value === e.target.value) {
document.getElementById(panel.value).style.display = 'block';
} else {
document.getElementById(panel.value).style.display = 'none';
}
});
}
};
// handle items which handle from select options
handleSelectedPanel = (e, subPanel) => {
{
subPanel.map((panel, i) => {
if (panel.id === e) {
document.getElementById(panel.id).style.display = 'block';
} else {
document.getElementById(panel.id).style.display = 'none';
}
});
}
};
// handle items which handle from checkbox
handleSubPanel = e => {
if (e.target.checked) {
let joined = this.state.activeSubPanelKeys.concat(e.target.id);
this.setState({ activeSubPanelKeys: joined });
} else {
let index = this.state.activeSubPanelKeys.indexOf(e.target.id);
if (index !== -1) {
this.state.activeSubPanelKeys.splice(index, 1);
let removed = this.state.activeSubPanelKeys;
this.setState({ activeSubPanelKeys: removed });
}
}
};
// handle Switch on off button
handleMainPanel = (e, ref) => {
if (e) {
let joined = this.state.activePanelKeys.concat(ref);
this.setState({ activePanelKeys: joined });
} else {
let index = this.state.activePanelKeys.indexOf(ref);
if (index !== -1) {
this.state.activePanelKeys.splice(index, 1);
let removed = this.state.activePanelKeys;
this.setState({ activePanelKeys: removed });
}
}
};
handleCustomInputTable = event => {
const { count, customInputDataArray } = this.state;
const newData = [
{
key: count,
CERT_NAME: `${event.file.name}`,
},
];
this.setState({
customInputDataArray: [...customInputDataArray, newData],
count: count + 1,
});
};
handleAdd = array => {
const { count, inputTableDataSources } = this.state;
const newData = [
{
key: count,
},
];
inputTableDataSources[array].push(newData);
Object.defineProperty(inputTableDataSources, array, {
value: inputTableDataSources[array],
});
this.setState({
inputTableDataSources,
count: count + 1,
});
};
getColumns = ({ getFieldDecorator }, arr) => {
const columnArray = [];
const actionColumn = [
{
title: '',
dataIndex: 'operation',
render: (name, row) => (
<Form.Item>
<Popconfirm title="Sure to delete?">
<a>
<Text type="danger">
<Icon type="delete" />
</Text>
</a>
</Popconfirm>
</Form.Item>
),
},
];
Object.values(arr).map((columnData, c) => {
if (columnData.type === 'input') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {})(
<Input
type={columnData.others.inputType}
placeholder={columnData.others.placeholder}
/>,
)}
</Form.Item>
),
};
columnArray.push(column);
} else if (columnData.type === 'upload') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {})(
<Upload>
<Button>
<Icon type="upload" /> Choose file
</Button>
</Upload>,
)}
</Form.Item>
),
};
columnArray.push(column);
} else if (columnData.type === 'select') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {
initialValue: columnData.others.initialDataIndex,
})(
<Select>
{columnData.others.option.map((option, i) => {
return (
<Option key={i} value={option.key}>
{option.value}
</Option>
);
})}
</Select>,
)}
</Form.Item>
),
};
columnArray.push(column);
}
});
const columns = columnArray.concat(actionColumn);
return columns;
};
// generate payload by adding policy configurations
onHandleContinue = (e, formname) => {
const allFields = this.props.form.getFieldsValue();
let activeFields = [];
// get currently active field list
for (let i = 0; i < this.state.activePanelKeys.length; i++) {
Object.keys(allFields).map(key => {
if (key.includes(`${this.state.activePanelKeys[i]}-`)) {
if (
subPanelpayloadAttributes.hasOwnProperty(
`${this.state.activePanelKeys[i]}`,
)
) {
Object.keys(
subPanelpayloadAttributes[this.state.activePanelKeys[i]],
).map(subPanel => {
if (`${this.state.activePanelKeys[i]}-${subPanel}` === true) {
if (key.includes(`-${subPanel}-`)) {
activeFields.push(key);
}
} else if (!key.includes(`-${subPanel}-`)) {
activeFields.push(key);
}
});
} else {
activeFields.push(key);
}
}
});
}
this.onFieldValidate(activeFields, formname);
};
onFieldValidate = (fields, formName) => {
// validate fields and get profile features list
this.props.form.validateFields(fields, (err, values) => {
if (!err) {
let profileFeaturesList = [];
for (let i = 0; i < this.state.activePanelKeys.length; i++) {
let content = {};
Object.entries(values).map(([key, value]) => {
if (key.includes(`${this.state.activePanelKeys[i]}-`)) {
content[
key.replace(`${this.state.activePanelKeys[i]}-`, '')
] = value;
}
});
let feature = {
featureCode: this.state.activePanelKeys[i],
deviceType: this.props.deviceType,
content: content,
};
profileFeaturesList.push(feature);
}
this.props.getPolicyPayloadData(formName, profileFeaturesList);
this.props.getNextStep();
}
});
};
// generate form items
getPanelItems = (panel, panelId) => {
const { getFieldDecorator } = this.props.form;
const subPanelList = {};
return panel.map((item, k) => {
switch (item.type) {
case 'select':
if (item.optional.hasOwnProperty('subPanel')) {
return (
<div>
<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}`, {
initialValue: `${item.optional.option[0].name}`,
})(
<Select
onChange={e =>
this.handleSelectedPanel(e, item.optional.subPanel)
}
>
{item.optional.option.map((option, i) => {
return (
<Option key={i} value={option.value}>
{option.name}
</Option>
);
})}
</Select>,
)}
</Form.Item>
<div className={'sub-panel-container'}>
{item.optional.subPanel.map((panel, i) => {
return (
<div
id={panel.id}
key={i}
style={
panel.id === item.optional.initialDataIndex
? { display: 'block' }
: { display: 'none' }
}
>
{this.getPanelItems(panel.panelItem)}
</div>
);
})}
</div>
</div>
);
}
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}`, {
initialValue: `${item.optional.option[0].name}`,
})(
<Select>
{item.optional.option.map((option, i) => {
return (
<Option key={i} value={option.value}>
{option.name}
</Option>
);
})}
</Select>,
)}
</Form.Item>
);
case 'timeSelector':
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}`, {
initialValue: item.optional.initialDataIndex,
})(
<Select>
{this.getOptionForTimeSelectors(
item.optional.firstOptionValue,
item.optional.lastOptionValue,
item.optional.valueDifference,
)}
</Select>,
)}
</Form.Item>
);
case 'input':
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}`,
},
],
})(<Input placeholder={item.optional.placeholder} />)}
</Form.Item>
);
case 'checkbox':
if (item.optional.hasOwnProperty('subPanel')) {
return (
<div key={k}>
<Collapse
bordered={false}
activeKey={this.state.activeSubPanelKeys}
>
<Collapse.Panel
key={item.id}
showArrow={false}
style={{ border: 0 }}
header={
<Form.Item key={k}>
{getFieldDecorator(`${item.id}`, {
valuePropName: 'checked',
initialValue: item.optional.ischecked,
})(
<Checkbox onChange={this.handleSubPanel}>
<span>
{item.label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>,
)}
</Form.Item>
}
>
<div>
<div>
{item.optional.subPanel.map((panel, i) => {
subPanelList[panel.others.itemSwitch] =
panel.others.itemPayload;
if (
subPanelpayloadAttributes.hasOwnProperty(panelId)
) {
Object.assign(
subPanelpayloadAttributes[panelId],
subPanelList,
);
} else {
subPanelpayloadAttributes[panelId] = subPanelList;
}
return (
<div key={i}>
{this.getPanelItems(panel.panelItem, panelId)}
</div>
);
})}
</div>
</div>
</Collapse.Panel>
</Collapse>
</div>
);
}
return (
<Form.Item key={k}>
{getFieldDecorator(`${item.id}`, {
valuePropName: 'checked',
initialValue: item.optional.ischecked,
})(
<Checkbox>
<span>
{item.label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>,
)}
</Form.Item>
);
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}`, {
initialValue: null,
})(
<TextArea
placeholder={item.optional.placeholder}
rows={item.optional.row}
/>,
)}
</Form.Item>
);
case 'radioGroup':
return (
<div>
<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}`, {
initialValue: `${item.optional.initialValue}`,
})(
<Radio.Group
onChange={e =>
this.handleRadioPanel(e, item.optional.radio)
}
>
{item.optional.radio.map((option, i) => {
return (
<Radio key={i} value={option.value}>
{option.name}
</Radio>
);
})}
</Radio.Group>,
)}
</Form.Item>
<div className={'sub-panel-container'}>
{item.optional.subPanel.map((panel, i) => {
return (
<div
key={i}
id={panel.id}
style={
panel.id === item.optional.initialValue
? { display: 'block' }
: { display: 'none' }
}
>
{this.getPanelItems(panel.panelItem)}
</div>
);
})}
</div>
</div>
);
case 'title':
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', {})(
<Upload>
<Button>
<Icon type="upload" /> Click to upload
</Button>
</Upload>,
)}
</Form.Item>
);
case 'inputTable':
if (
!(`${item.optional.dataSource}` in this.state.inputTableDataSources)
) {
Object.defineProperty(
this.state.inputTableDataSources,
`${item.optional.dataSource}`,
{ value: [], writable: true },
);
}
return (
<div key={k}>
<Button
onClick={() => this.handleAdd(item.optional.dataSource)}
type="primary"
style={{ marginBottom: 16 }}
>
<Icon type="plus-circle" />
{item.optional.button.name}
</Button>
<Table
id={item.id}
dataSource={
this.state.inputTableDataSources[item.optional.dataSource]
}
columns={this.getColumns(
{ getFieldDecorator },
item.optional.columns,
)}
/>
</div>
);
case 'customInputTable':
return (
<div key={k}>
<Upload onChange={this.handleCustomInputTable}>
<Button type="primary" style={{ marginBottom: 16 }}>
<Icon type="plus-circle" />
{item.optional.button.name}
</Button>
</Upload>
<Table
id={item.id}
dataSource={this.state.customInputDataArray}
columns={this.getColumns(
{ getFieldDecorator },
item.optional.columns,
)}
/>
</div>
);
default:
return null;
}
});
};
render() {
const { policyUIConfigurationsList } = this.props;
const { getFieldDecorator } = this.props.form;
return (
<div className="tab-container">
<Tabs
tabPosition={'left'}
size={'large'}
onTabClick={this.setProfileInfo}
>
{policyUIConfigurationsList.map((element, i) => {
return (
<TabPane tab={<span>{element.name}</span>} key={i}>
{Object.values(element.panels).map((panel, j) => {
panel = panel.panel;
return (
<div key={j}>
<Collapse
bordered={false}
activeKey={this.state.activePanelKeys}
>
<Collapse.Panel
key={panel.panelId}
showArrow={false}
style={{ border: 0 }}
header={
<div>
<Row>
<Col offset={0} span={14}>
<Title level={4}> {panel.title} </Title>
</Col>
<Col offset={8} span={1}>
<Switch
checkedChildren="ON"
unCheckedChildren="OFF"
onChange={e =>
this.handleMainPanel(
e,
`${panel.panelId}`,
)
}
/>
</Col>
</Row>
<Row>{panel.description}</Row>
</div>
}
>
{panel.hasOwnProperty('panelItem') && (
<div>
<Form name={panel.panelId}>
<Form.Item style={{ display: 'none' }}>
{getFieldDecorator(`${panel.panelId}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(
panel.panelItem,
panel.panelId,
)}
</Form>
</div>
)}
{panel.hasOwnProperty('subFormLists') && (
<div>
{Object.values(panel.subFormLists).map(
(form, i) => {
return (
<Form name={form.id} key={i}>
<Form.Item style={{ display: 'none' }}>
{getFieldDecorator(`${form.id}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(form.panelItem)}
</Form>
);
},
)}
</div>
)}
</Collapse.Panel>
</Collapse>
</div>
);
})}
</TabPane>
);
})}
</Tabs>
<Col span={16} offset={20}>
<div style={{ marginTop: 24 }}>
<Button style={{ marginRight: 8 }} onClick={this.props.getPrevStep}>
Back
</Button>
<Button
type="primary"
onClick={e => this.onHandleContinue(e, 'configureProfileData')}
>
Continue
</Button>
</div>
</Col>
</div>
);
}
}
export default withConfigContext(Form.create()(ConfigureProfile));

@ -0,0 +1,86 @@
/*
* Copyright (c) 2020, 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 { withConfigContext } from '../../../../../../../../components/ConfigContext';
import { Button, Col, Form, Input } from 'antd';
const { TextArea } = Input;
class PublishDevices extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
}
onClickSavePolicy = (event, formName) => {
this.props.form.validateFields((err, values) => {
if (!err) {
this.props.getPolicyPayloadData(formName, values);
}
});
};
render() {
const { policyData } = this.props;
const { getFieldDecorator } = this.props.form;
return (
<div>
<Form.Item
label={'Set a name to your policy *'}
style={{ display: 'block' }}
>
{getFieldDecorator('policyName', {
initialValue: policyData.policyName,
rules: [
{
pattern: new RegExp('^.{1,30}$'),
message: 'Should be 1-to-30 characters long',
},
],
})(<Input placeholder={'Should be 1 to 30 characters long'} />)}
</Form.Item>
<Form.Item label={'Add a Description'} style={{ display: 'block' }}>
{getFieldDecorator('description', {
initialValue: policyData.description,
})(<TextArea rows={8} />)}
</Form.Item>
<Col span={16} offset={18}>
<div style={{ marginTop: 24 }}>
<Button style={{ marginRight: 8 }} onClick={this.props.getPrevStep}>
Back
</Button>
<Button
type="primary"
style={{ marginRight: 8 }}
onClick={e => this.onClickSavePolicy(e, 'publishDevicesData')}
>
Save & Publish
</Button>
<Button
type="primary"
onClick={e => this.onClickSavePolicy(e, 'publishDevicesData')}
>
Save
</Button>
</div>
</Col>
</div>
);
}
}
export default withConfigContext(Form.create()(PublishDevices));

@ -0,0 +1,182 @@
/*
* Copyright (c) 2020, 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 {
Button,
Col,
Form,
Icon,
message,
notification,
Radio,
Select,
Tooltip,
} from 'antd';
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
import axios from 'axios';
const { Option } = Select;
class SelectPolicyType extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
correctivePoliciesList: [],
};
}
componentDidMount() {
this.fetchPolicies();
}
// generate payload using Select policy type
onHandleContinue = (e, formName) => {
this.props.form.validateFields((err, values) => {
if (!err) {
if (
values.policyType === 'CORRECTIVE' ||
values.correctiveActions === 'NONE'
) {
values.correctiveActions = [];
}
this.props.getPolicyPayloadData(formName, values);
this.props.getNextStep();
}
});
};
fetchPolicies = () => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies';
// send request to the invokerss
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
let policies = res.data.data.policies;
let correctivePolicies = [];
for (let i = 0; i < policies.length; i++) {
if (policies[i].policyType === 'CORRECTIVE') {
correctivePolicies.push(
<Option key={policies[i].profileId}>
{policies[i].policyName}
</Option>,
);
}
}
this.setState({
correctivePoliciesList: correctivePolicies,
});
}
})
.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 });
});
};
handlePolicyTypes = event => {
if (event.target.value === 'GENERAL') {
document.getElementById('generalPolicySubPanel').style.display = 'block';
} else {
document.getElementById('generalPolicySubPanel').style.display = 'none';
}
};
render() {
const { policyData } = this.props;
const { getFieldDecorator } = this.props.form;
let correctiveActions = null;
if (policyData.correctiveActions.length > 0) {
correctiveActions = '';
} else {
correctiveActions = 'NONE';
}
return (
<div>
<Form.Item style={{ display: 'block' }}>
{getFieldDecorator('policyType', {
initialValue: policyData.policyType,
})(
<Radio.Group onChange={this.handlePolicyTypes}>
<Radio value="GENERAL">General Policy</Radio>
<Radio value="CORRECTIVE">Corrective Policy</Radio>
</Radio.Group>,
)}
</Form.Item>
<div id="generalPolicySubPanel" style={{ display: 'block' }}>
<Form.Item
label={
<span>
Select Corrective Policy&nbsp;
<Tooltip
title={
'Select the corrective policy to be applied when this general policy is violated'
}
placement="right"
>
<Icon type="question-circle-o" />
</Tooltip>
</span>
}
>
{getFieldDecorator('correctiveActions', {
initialValue: correctiveActions,
})(
<Select style={{ width: '100%' }}>
<Option value="NONE">None</Option>
{this.state.correctivePoliciesList}
</Select>,
)}
</Form.Item>
</div>
<Col span={16} offset={20}>
<div style={{ marginTop: 24 }}>
<Button style={{ marginRight: 8 }} onClick={this.props.getPrevStep}>
Back
</Button>
<Button
type="primary"
onClick={e => this.onHandleContinue(e, 'policyTypeData')}
>
Continue
</Button>
</div>
</Col>
</div>
);
}
}
export default withConfigContext(Form.create()(SelectPolicyType));

@ -0,0 +1,273 @@
/*
* Copyright (c) 2020, 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 { Form, Row, Col, Card, Steps, message, notification } from 'antd';
import { withConfigContext } from '../../../../../../components/ConfigContext';
import ConfigureProfile from './components/ConfigureProfile';
import SelectPolicyType from './components/SelectPolicyType';
import AssignGroups from './components/AssignGroups';
import PublishDevices from './components/PublishDevices';
import axios from 'axios';
const { Step } = Steps;
class EditPolicy extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
currentStepIndex: 0,
policyUIConfigurationsList: null,
newPolicyPayload: { compliance: 'enforce' },
policyProfile: {},
payloadData: {},
policyFeatureList: [],
policyData: {},
deviceType: null,
};
}
componentDidMount() {
this.getSelectedPolicy(this.props.policyId);
}
getPolicyPayloadData = (dataName, dataValue) => {
Object.defineProperty(this.state.payloadData, dataName, {
value: dataValue,
writable: true,
});
if (dataName === 'publishDevicesData') {
this.createPayload();
}
};
createPayload = () => {
const {
publishDevicesData,
configureProfileData,
policyTypeData,
groupData,
} = this.state.payloadData;
const profile = {
profileName: publishDevicesData.policyName,
deviceType: this.state.deviceType,
profileFeaturesList: configureProfileData,
};
const payload = {
...publishDevicesData,
compliance: 'enforce',
ownershipType: null,
...policyTypeData,
profile: profile,
...groupData,
};
this.onEditPolicy(JSON.stringify(payload));
};
getSelectedPolicy = policyId => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/' +
policyId;
// send request to the invokers
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
isLoading: true,
policyData: res.data.data,
deviceType: res.data.data.profile.deviceType,
policyFeatureList: res.data.data.profile.profileFeaturesList,
});
this.getPolicyConfigJson(res.data.data.profile.deviceType);
}
})
.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 selected policy.',
});
}
});
};
getPolicyConfigJson = type => {
this.setState({ isLoading: true });
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/device-types/' +
type +
'/ui-policy-configurations';
// send request to the invokers
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
policyUIConfigurationsList: JSON.parse(res.data.data),
});
}
})
.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 Policy details.',
});
}
});
};
onEditPolicy = value => {
axios
.put(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/' +
this.props.policyId,
value,
{ headers: { 'Content-Type': 'application-json' } },
)
.then(res => {
if (res.status === 200) {
notification.success({
message: 'Done',
duration: 4,
description: 'Successfully Updated the Policy.',
});
}
})
.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 Updated the Policy.',
});
}
});
};
getNextStep = () => {
const currentStepIndex = this.state.currentStepIndex + 1;
this.setState({ currentStepIndex });
};
getPrevStep = () => {
const currentStepIndex = this.state.currentStepIndex - 1;
this.setState({ currentStepIndex });
};
render() {
const {
currentStepIndex,
policyUIConfigurationsList,
policyFeatureList,
policyData,
deviceType,
} = this.state;
return (
<div>
{policyUIConfigurationsList != null && (
<Row>
<Col span={20} offset={2}>
<Steps style={{ minHeight: 32 }} current={currentStepIndex}>
<Step key="ProfileConfigure" title="Configure profile" />
<Step key="PolicyType" title="Select policy type" />
<Step key="AssignGroups" title="Assign to groups" />
<Step key="Publish" title="Publish to devices" />
</Steps>
</Col>
<Col span={16} offset={4}>
<Card style={{ marginTop: 24 }}>
<div
style={{ display: currentStepIndex === 0 ? 'unset' : 'none' }}
>
<ConfigureProfile
policyUIConfigurationsList={policyUIConfigurationsList}
getPolicyPayloadData={this.getPolicyPayloadData}
getNextStep={this.getNextStep}
policyFeatureList={policyFeatureList}
deviceType={deviceType}
/>
</div>
<div
style={{ display: currentStepIndex === 1 ? 'unset' : 'none' }}
>
<SelectPolicyType
getPolicyPayloadData={this.getPolicyPayloadData}
policyData={policyData}
getPrevStep={this.getPrevStep}
getNextStep={this.getNextStep}
/>
</div>
<div
style={{ display: currentStepIndex === 2 ? 'unset' : 'none' }}
>
<AssignGroups
getPolicyPayloadData={this.getPolicyPayloadData}
policyData={policyData}
getPrevStep={this.getPrevStep}
getNextStep={this.getNextStep}
/>
</div>
<div
style={{ display: currentStepIndex === 3 ? 'unset' : 'none' }}
>
<PublishDevices
policyData={policyData}
getPolicyPayloadData={this.getPolicyPayloadData}
getPrevStep={this.getPrevStep}
/>
</div>
</Card>
</Col>
</Row>
)}
</div>
);
}
}
export default withConfigContext(
Form.create({ name: 'edit-policy' })(EditPolicy),
);

@ -0,0 +1,55 @@
/*
* Copyright (c) 2020, 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 { Divider, Icon, Tooltip } from 'antd';
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
import { Link } from 'react-router-dom';
class PolicyAction extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
}
render() {
return (
<div>
<div>
<Tooltip placement="top" title={'Edit Policy'}>
<Link
to={`/entgra/policy/edit/${this.props.selectedPolicyData.id}`}
>
<Icon type="edit" />
</Link>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="top" title={''}>
<Link
to={`/entgra/policy/view/${this.props.selectedPolicyData.id}`}
>
<Icon type="eye" />
</Link>
</Tooltip>
</div>
</div>
);
}
}
export default withConfigContext(PolicyAction);

@ -0,0 +1,170 @@
/*
* Copyright (c) 2020, 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 { Button, Tooltip, Popconfirm, Divider } from 'antd';
class BulkActionBar extends React.Component {
constructor(props) {
super(props);
this.state = {
selectedMultiple: false,
selectedSingle: false,
isPolicyActive: true,
};
}
// This method checks whether active devices are selected
onCheckPolicyStatus = () => {
let tempIsPolicyActive;
for (let i = 0; i < this.props.selectedRows.length; i++) {
if (this.props.selectedRows[i].active) {
tempIsPolicyActive = true;
break;
}
tempIsPolicyActive = false;
}
this.setState({ isPolicyActive: tempIsPolicyActive });
};
onConfirmRemove = () => {
if (!this.state.isPolicyActive) {
this.props.removePolicy();
}
};
onConfirmPublish = () => {
if (!this.state.isPolicyActive) {
this.props.publishPolicy();
}
};
onConfirmUnpublish = () => {
if (this.state.isPolicyActive) {
this.props.unpublishPolicy();
}
};
render() {
const isSelected = this.props.selectedRows.length > 0;
return (
<div>
<div style={{ padding: '8px' }}>
<Tooltip placement="bottom" title={'Apply Changes to Device'}>
<Popconfirm
placement="topLeft"
title={'Do you really want to apply changes to all policies?'}
onConfirm={this.props.applyChanges}
okText="Yes"
cancelText="No"
>
<Button
type="link"
shape="circle"
icon="check-circle"
size={'default'}
style={{ margin: '2px' }}
/>
</Popconfirm>
</Tooltip>
</div>
<div
style={{ display: isSelected ? 'inline' : 'none', padding: '8px' }}
>
<Tooltip
placement="bottom"
title={'Remove'}
autoAdjustOverflow={true}
>
<Popconfirm
placement="topLeft"
title={
!this.state.isPolicyActive
? 'Do you really want to remove the selected policy(s)?'
: 'You cannot select already active policies. Please deselect active policies and try again.'
}
onConfirm={this.onConfirmRemove}
okText="Yes"
cancelText="No"
>
<Button
type="link"
shape="circle"
icon="delete"
size={'default'}
onClick={this.onCheckPolicyStatus}
style={{ margin: '2px' }}
/>
</Popconfirm>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={'Publish'}>
<Popconfirm
placement="topLeft"
title={
!this.state.isPolicyActive
? 'Do you really want to publish the selected policy(s)??'
: 'You cannot select already active policies. Please deselect active policies and try again.'
}
okText="Yes"
onConfirm={this.onConfirmPublish}
cancelText="No"
>
<Button
type="link"
shape="circle"
icon="import"
onClick={this.onCheckPolicyStatus}
size={'default'}
style={{
margin: '2px',
}}
/>
</Popconfirm>
</Tooltip>
<Divider type="vertical" />
<Tooltip placement="bottom" title={'Unpublish'}>
<Popconfirm
placement="topLeft"
title={
this.state.isPolicyActive
? 'Do you really want to unpublish the selected policy(s)?'
: 'You cannot select already inactive policies to be unpublished. Please deselect inactive policies and try again.'
}
okText="Yes"
onConfirm={this.onConfirmUnpublish}
cancelText="No"
>
<Button
type="link"
shape="circle"
icon="export"
onClick={this.onCheckPolicyStatus}
size={'default'}
style={{ margin: '2px' }}
/>
</Popconfirm>
</Tooltip>
</div>
</div>
);
}
}
export default BulkActionBar;

@ -24,42 +24,16 @@ import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules. // Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en'; import en from 'javascript-time-ago/locale/en';
import { withConfigContext } from '../../../../../../components/ConfigContext'; import { withConfigContext } from '../../../../../../components/ConfigContext';
import PolicyAction from './component/PolicyAction';
import PolicyBulkActionBar from './component/PolicyBulkActionBar';
let apiUrl; 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
},
];
class PoliciesTable extends React.Component { class PoliciesTable extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
TimeAgo.addLocale(en); TimeAgo.addLocale(en);
this.config = this.props.context;
this.state = { this.state = {
data: [], data: [],
pagination: {}, pagination: {},
@ -149,10 +123,203 @@ class PoliciesTable extends React.Component {
}); });
}; };
unpublishPolicy = () => {
const policyIDs = this.state.selectedRows.map(obj => obj.id);
// send request to the invoker
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/deactivate-policy',
policyIDs,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Selected policy(s) was successfully unpublished',
});
}
})
.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 unpublish policy(s).',
});
}
});
};
publishPolicy = () => {
const policyIDs = this.state.selectedRows.map(obj => obj.id);
// send request to the invoker
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/activate-policy',
policyIDs,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Selected policy(s) was successfully unpublished',
});
}
})
.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 unpublish policy(s).',
});
}
});
};
removePolicy = () => {
const policyIDs = this.state.selectedRows.map(obj => obj.id);
// send request to the invoker
axios
.post(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/remove-policy',
policyIDs,
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Selected policy(s) was successfully removed.',
});
}
})
.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 remove policy(s).',
});
}
});
};
applyChanges = () => {
// send request to the invoker
axios
.put(
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/apply-changes',
'null',
{ headers: { 'Content-Type': 'application/json' } },
)
.then(res => {
if (res.status === 200) {
this.fetchGroups();
notification.success({
message: 'Done',
duration: 4,
description: 'Changes applied successfully.',
});
}
})
.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 apply changes to device.',
});
}
});
};
render() { render() {
const { data, pagination, loading } = this.state; const { data, pagination, loading, selectedRows } = this.state;
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
},
{
title: 'Action',
dataIndex: 'id',
key: 'action',
render: (id, row) => (
<span>
<PolicyAction selectedPolicyData={row} />
</span>
),
},
];
return ( return (
<div> <div>
<PolicyBulkActionBar
selectedRows={selectedRows}
unpublishPolicy={this.unpublishPolicy}
publishPolicy={this.publishPolicy}
removePolicy={this.removePolicy}
applyChanges={this.applyChanges}
/>
<Table <Table
columns={columns} columns={columns}
rowKey={record => record.id} rowKey={record => record.id}

@ -0,0 +1,756 @@
/*
* Copyright (c) 2020, 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 {
Alert,
Button,
Checkbox,
Col,
Collapse,
Form,
Icon,
Input,
Popconfirm,
Radio,
Row,
Select,
Table,
Tabs,
Tooltip,
Typography,
Upload,
} from 'antd';
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
import moment from 'moment';
const { Text, Title, Paragraph } = Typography;
const { TabPane } = Tabs;
const { Option } = Select;
const { TextArea } = Input;
const subPanelpayloadAttributes = {};
const fieldKeys = [];
class PolicyInfo extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
data: {},
policyFeatureList: [],
activePanelKeys: [],
profilePreviewKey: '',
customInputDataArray: [],
inputTableDataSources: {},
isInfoPreview: false,
};
}
setProfileInfo = e => {
let activePolicies = [];
let activePolicyFields = {};
const allFields = this.props.form.getFieldsValue();
this.props.policyFeatureList.map(element => {
activePolicies.push(element.featureCode);
let featureData = JSON.parse(element.content);
Object.keys(featureData).map(key => {
let regex = new RegExp(`${element.featureCode}.+${key}`, 'g');
Object.keys(allFields).map(fieldName => {
if (fieldName.match(regex) != null) {
activePolicyFields[fieldName] = featureData[key];
}
});
});
});
this.props.form.setFieldsValue(activePolicyFields);
this.setState({
activePanelKeys: activePolicies,
});
};
// convert time from 24h format to 12h format
timeConverter = time => {
time = time
.toString()
.match(/^([01]\d|2[0-3])(:)([0-5]\d)(:[0-5]\d)?$/) || [time];
if (time.length > 1) {
time = time.slice(1);
time[5] = +time[0] < 12 ? ' AM' : ' PM';
time[0] = +time[0] % 12 || 12;
}
return time.join('');
};
// get Option value from start Time, end Time and time difference between 2 values
getOptionForTimeSelectors = (startTimeValue, endTimeValue, timeIncrement) => {
let timeOptions = [];
let time = new Date(
moment()
.startOf('day')
.format('YYYY/MM/DD'),
);
let tempValue = startTimeValue;
time.setMinutes(time.getMinutes() + tempValue);
let startOption = (
<Option value={String(tempValue)}>
{this.timeConverter(
`${String(time)
.split(' ')[4]
.substring(0, 5)}`,
)}
</Option>
);
timeOptions.push(startOption);
while (tempValue !== endTimeValue) {
time = new Date(
moment()
.startOf('day')
.format('YYYY/MM/DD'),
);
tempValue += timeIncrement;
if (tempValue > 1440) {
tempValue = 0;
continue;
}
time.setMinutes(time.getMinutes() + tempValue);
let option = (
<Option value={String(tempValue)}>
{this.timeConverter(
`${String(time)
.split(' ')[4]
.substring(0, 5)}`,
)}
</Option>
);
timeOptions.push(option);
}
return timeOptions;
};
// handle items which handle from radio buttons
handleRadioPanel = (e, subPanel) => {
{
subPanel.map((panel, i) => {
if (panel.value === e.target.value) {
document.getElementById(panel.value).style.display = 'block';
} else {
document.getElementById(panel.value).style.display = 'none';
}
});
}
};
// handle items which handle from select options
handleSelectedPanel = (e, subPanel) => {
{
subPanel.map((panel, i) => {
if (panel.id === e) {
document.getElementById(panel.id).style.display = 'block';
} else {
document.getElementById(panel.id).style.display = 'none';
}
});
}
};
getColumns = ({ getFieldDecorator }, arr) => {
const columnArray = [];
const actionColumn = [
{
title: '',
dataIndex: 'operation',
render: (name, row) => (
<Form.Item>
<Popconfirm title="Sure to delete?">
<a>
<Text type="danger">
<Icon type="delete" />
</Text>
</a>
</Popconfirm>
</Form.Item>
),
},
];
Object.values(arr).map((columnData, c) => {
if (columnData.type === 'input') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {})(
<Input
type={columnData.others.inputType}
placeholder={columnData.others.placeholder}
/>,
)}
</Form.Item>
),
};
columnArray.push(column);
} else if (columnData.type === 'upload') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {})(
<Upload>
<Button>
<Icon type="upload" /> Choose file
</Button>
</Upload>,
)}
</Form.Item>
),
};
columnArray.push(column);
} else if (columnData.type === 'select') {
const column = {
title: `${columnData.name}`,
dataIndex: `${columnData.key}`,
key: `${columnData.key}`,
render: (name, row, i) => (
<Form.Item>
{getFieldDecorator(`${columnData.key}${i}`, {
initialValue: columnData.others.initialDataIndex,
})(
<Select>
{columnData.others.option.map((option, i) => {
return (
<Option key={i} value={option.key}>
{option.value}
</Option>
);
})}
</Select>,
)}
</Form.Item>
),
};
columnArray.push(column);
}
});
const columns = columnArray.concat(actionColumn);
return columns;
};
// generate form items
getPanelItems = (panel, panelId) => {
const { getFieldDecorator } = this.props.form;
const subPanelList = {};
return panel.map((item, k) => {
fieldKeys.push(item.id);
switch (item.type) {
case 'select':
if (item.optional.hasOwnProperty('subPanel')) {
return (
<div>
<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}`, {
initialValue: `${item.optional.option[0].name}`,
})(
<Select
onChange={e =>
this.handleSelectedPanel(e, item.optional.subPanel)
}
>
{item.optional.option.map((option, i) => {
return (
<Option key={i} value={option.value}>
{option.name}
</Option>
);
})}
</Select>,
)}
</Form.Item>
<div className={'sub-panel-container'}>
{item.optional.subPanel.map((panel, i) => {
return (
<div
id={panel.id}
key={i}
style={
panel.id === item.optional.initialDataIndex
? { display: 'block' }
: { display: 'none' }
}
>
{this.getPanelItems(panel.panelItem)}
</div>
);
})}
</div>
</div>
);
}
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}`, {
initialValue: `${item.optional.option[0].name}`,
})(
<Select>
{item.optional.option.map((option, i) => {
return (
<Option key={i} value={option.value}>
{option.name}
</Option>
);
})}
</Select>,
)}
</Form.Item>
);
case 'timeSelector':
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}`, {
initialValue: item.optional.initialDataIndex,
})(
<Select>
{this.getOptionForTimeSelectors(
item.optional.firstOptionValue,
item.optional.lastOptionValue,
item.optional.valueDifference,
)}
</Select>,
)}
</Form.Item>
);
case 'input':
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}`,
},
],
})(<Input placeholder={item.optional.placeholder} disabled />)}
</Form.Item>
);
case 'checkbox':
if (item.optional.hasOwnProperty('subPanel')) {
return (
<div key={k}>
<Collapse
bordered={false}
activeKey={this.state.activeSubPanelKeys}
>
<Collapse.Panel
key={item.id}
showArrow={false}
style={{ border: 0 }}
header={
<Form.Item key={k}>
{getFieldDecorator(`${item.id}`, {
valuePropName: 'checked',
initialValue: item.optional.ischecked,
})(
<Checkbox>
<span>
{item.label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>,
)}
</Form.Item>
}
>
<div>
<div>
{item.optional.subPanel.map((panel, i) => {
subPanelList[panel.others.itemSwitch] =
panel.others.itemPayload;
if (
subPanelpayloadAttributes.hasOwnProperty(panelId)
) {
Object.assign(
subPanelpayloadAttributes[panelId],
subPanelList,
);
} else {
subPanelpayloadAttributes[panelId] = subPanelList;
}
return (
<div key={i}>
{this.getPanelItems(panel.panelItem, panelId)}
</div>
);
})}
</div>
</div>
</Collapse.Panel>
</Collapse>
</div>
);
}
return (
<Form.Item key={k}>
{getFieldDecorator(`${item.id}`, {
valuePropName: 'checked',
initialValue: item.optional.ischecked,
})(
<Checkbox>
<span>
{item.label}&nbsp;
<Tooltip title={item.tooltip} placement="right">
<Icon type="question-circle-o" />
</Tooltip>
</span>
</Checkbox>,
)}
</Form.Item>
);
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}`, {
initialValue: null,
})(
<TextArea
placeholder={item.optional.placeholder}
rows={item.optional.row}
disabled
/>,
)}
</Form.Item>
);
case 'radioGroup':
return (
<div>
<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}`, {
initialValue: `${item.optional.initialValue}`,
})(
<Radio.Group
onChange={e =>
this.handleRadioPanel(e, item.optional.radio)
}
>
{item.optional.radio.map((option, i) => {
return (
<Radio key={i} value={option.value}>
{option.name}
</Radio>
);
})}
</Radio.Group>,
)}
</Form.Item>
<div className={'sub-panel-container'}>
{item.optional.subPanel.map((panel, i) => {
return (
<div
key={i}
id={panel.id}
style={
panel.id === item.optional.initialValue
? { display: 'block' }
: { display: 'none' }
}
>
{this.getPanelItems(panel.panelItem)}
</div>
);
})}
</div>
</div>
);
case 'title':
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', {})(
<Upload>
<Button>
<Icon type="upload" /> Click to upload
</Button>
</Upload>,
)}
</Form.Item>
);
case 'inputTable':
if (
!(`${item.optional.dataSource}` in this.state.inputTableDataSources)
) {
Object.defineProperty(
this.state.inputTableDataSources,
`${item.optional.dataSource}`,
{ value: [], writable: true },
);
}
return (
<div key={k}>
<Table
id={item.id}
dataSource={
this.state.inputTableDataSources[item.optional.dataSource]
}
columns={this.getColumns(
{ getFieldDecorator },
item.optional.columns,
)}
/>
</div>
);
case 'customInputTable':
return (
<div key={k}>
<Table
id={item.id}
dataSource={this.state.customInputDataArray}
columns={this.getColumns(
{ getFieldDecorator },
item.optional.columns,
)}
/>
</div>
);
default:
return null;
}
});
};
onPreview = e => {
this.setState({
profilePreviewKey: 'profileInfo',
isInfoPreview: true,
});
};
render() {
const { policyUIConfigurationsList } = this.props;
const { getFieldDecorator } = this.props.form;
return (
<div style={{ marginTop: 20 }}>
<Row>
<Col span={4}>
<Title level={4}>Profile Information</Title>
</Col>
<Col span={16}>
<Button type="link" icon="eye" onClick={this.onPreview}>
<Text
style={{
fontSize: 'small',
display: this.state.isInfoPreview ? 'none' : 'inline',
}}
>
(Click to view policy information)
</Text>
</Button>
</Col>
</Row>
<Collapse
bordered={false}
activeKey={this.state.profilePreviewKey}
style={{ display: this.state.isInfoPreview ? 'block' : 'none' }}
>
<Collapse.Panel
key={'profileInfo'}
showArrow={false}
style={{
border: 0,
}}
>
<div className="tab-container">
<Tabs
tabPosition={'left'}
size={'large'}
onChange={this.setProfileInfo}
>
{policyUIConfigurationsList.map((element, i) => {
return (
<TabPane tab={<span>{element.name}</span>} key={i}>
{Object.values(element.panels).map((panel, j) => {
panel = panel.panel;
return (
<div key={j}>
<Collapse
bordered={false}
activeKey={this.state.activePanelKeys}
>
<Collapse.Panel
key={panel.panelId}
showArrow={false}
style={{ border: 0 }}
header={
<div>
<Row>
<Col offset={0} span={14}>
<Title level={4}> {panel.title} </Title>
</Col>
</Row>
<Row>{panel.description}</Row>
</div>
}
>
{panel.hasOwnProperty('panelItem') && (
<div>
<Form name={panel.panelId}>
<Form.Item style={{ display: 'none' }}>
{getFieldDecorator(`${panel.panelId}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(
panel.panelItem,
panel.panelId,
)}
</Form>
</div>
)}
{panel.hasOwnProperty('subFormLists') && (
<div>
{Object.values(panel.subFormLists).map(
(form, i) => {
return (
<Form name={form.id} key={i}>
<Form.Item
style={{ display: 'none' }}
>
{getFieldDecorator(`${form.id}`, {
initialValue: ' ',
})(<Input />)}
</Form.Item>
{this.getPanelItems(form.panelItem)}
</Form>
);
},
)}
</div>
)}
</Collapse.Panel>
</Collapse>
</div>
);
})}
</TabPane>
);
})}
</Tabs>
</div>
</Collapse.Panel>
</Collapse>
</div>
);
}
}
export default withConfigContext(Form.create()(PolicyInfo));

@ -0,0 +1,131 @@
/*
* Copyright (c) 2020, 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 { Card, Col, Icon, Row, Typography } from 'antd';
import { withConfigContext } from '../../../../../../../../components/ConfigContext';
const { Title, Text } = Typography;
class ProfileOverview extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
data: {},
};
}
render() {
const { policyData } = this.props;
return (
<div>
<div style={{ marginTop: 20 }}>
<div>
<Title level={4}>Policy Overview</Title>
</div>
<Card>
<div style={{ paddingLeft: 4 }}>
<Row style={{ marginTop: 8 }}>
<Col span={8}>
<Text>Platform</Text>
</Col>
<Col span={8}>
<Text>{policyData.profile.deviceType.toUpperCase()}</Text>
</Col>
</Row>
<hr style={{ backgroundColor: 'grey' }} />
<Row style={{ marginTop: 15 }}>
<Col span={8}>
<Text>Groups</Text>
</Col>
<Col span={8}>
<Text>{policyData.deviceGroups}</Text>
</Col>
</Row>
<hr />
<Row style={{ marginTop: 15 }}>
<Col span={8}>
<Text>Action upon non-compliance</Text>
</Col>
<Col span={8}>
<Text>{policyData.compliance.toUpperCase()}</Text>
</Col>
</Row>
<hr />
<Row style={{ marginTop: 15 }}>
<Col span={8}>
<Text>Status</Text>
</Col>
<Col span={8}>
<Text>
{policyData.active ? (
<span>
<Icon
type="exclamation-circle"
style={{ color: '#6ab04c' }}
/>
Active
</span>
) : (
<span>
<Icon
type="exclamation-circle"
style={{ color: '#f9ca24' }}
/>
Inactive
</span>
)}
{policyData.updated ? <span>/Updated</span> : <span></span>}
</Text>
</Col>
</Row>
<hr />
<Row style={{ marginTop: 15 }}>
<Col span={8}>
<Text>Assigned Roles</Text>
</Col>
<Col span={8}>{policyData.roles}</Col>
</Row>
<hr />
<Row style={{ marginTop: 15 }}>
<Col span={8}>
<Text type={8}>Policy Type</Text>
</Col>
<Col span={8}>
<Text>{policyData.policyType}</Text>
</Col>
</Row>
</div>
</Card>
</div>
<div style={{ marginTop: 20 }}>
<Title level={4}>Description</Title>
<Card>
<Row>
<Col span={4}>
<Text>{policyData.description}</Text>
</Col>
</Row>
</Card>
</div>
</div>
);
}
}
export default withConfigContext(ProfileOverview);

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020, 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 { Col, message, notification } from 'antd';
import { withConfigContext } from '../../../../../../components/ConfigContext';
import PolicyInfo from './component/PolicyInfo';
import ProfileOverview from './component/ProfileOverview';
import axios from 'axios';
class PolicyProfile extends React.Component {
constructor(props) {
super(props);
this.config = this.props.context;
this.state = {
policyId: this.props.policyId,
policyData: null,
policyUIConfigurationsList: [],
policyFeatureList: [],
};
}
componentDidMount() {
this.getSelectedPolicy(this.props.policyId);
}
getSelectedPolicy = policyId => {
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/policies/' +
policyId;
// send request to the invokers
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
policyData: res.data.data,
policyFeatureList: res.data.data.profile.profileFeaturesList,
});
this.getPolicyConfigJson(res.data.data.profile.deviceType);
}
})
.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 selected policy.',
});
}
});
};
getPolicyConfigJson = type => {
this.setState({ isLoading: true });
let apiUrl =
window.location.origin +
this.config.serverConfig.invoker.uri +
this.config.serverConfig.invoker.deviceMgt +
'/device-types/' +
type +
'/ui-policy-configurations';
// send request to the invokers
axios
.get(apiUrl)
.then(res => {
if (res.status === 200) {
this.setState({
isLoading: false,
policyUIConfigurationsList: JSON.parse(res.data.data),
});
}
})
.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 Policy details.',
});
}
this.setState({ isLoading: false });
});
};
render() {
const {
policyData,
policyUIConfigurationsList,
policyFeatureList,
} = this.state;
return (
<div>
<Col span={16} offset={4}>
{/* <Card style={{ marginTop: 24 }}>*/}
<div>
{policyData != null && (
<ProfileOverview
policyId={this.props.policyId}
policyData={policyData}
/>
)}
</div>
<div>
{policyData != null && (
<PolicyInfo
policyId={this.state.policyId}
policyFeatureList={policyFeatureList}
policyUIConfigurationsList={policyUIConfigurationsList}
/>
)}
</div>
</Col>
</div>
);
}
}
export default withConfigContext(PolicyProfile);

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020, 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, { Component } from 'react';
import { Breadcrumb, Icon, PageHeader } from 'antd';
import { Link } from 'react-router-dom';
import EditPolicy from '../../components/EditPolicy';
import { withConfigContext } from '../../../../../../components/ConfigContext';
class EditSelectedPolicy extends Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
this.config = this.props.context;
this.state = {
data: {},
policyOverview: {},
policyId: '',
};
}
render() {
const {
match: { params },
} = this.props;
return (
<div>
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Policies</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
{/* <h3>Policies</h3>*/}
{/* <Paragraph>Create new policy on IoT Server.</Paragraph>*/}
</div>
<div style={{ borderRadius: 5 }}>
<EditPolicy policyId={params.policyId} />
</div>
</PageHeader>
<div
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
</div>
);
}
}
export default withConfigContext(EditSelectedPolicy);

@ -0,0 +1,68 @@
/*
* Copyright (c) 2020, 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, { Component } from 'react';
import { Breadcrumb, Icon, PageHeader } from 'antd';
import { Link } from 'react-router-dom';
import { withConfigContext } from '../../../../../../components/ConfigContext';
import PolicyProfile from '../../components/PolicyProfile';
class ViewPolicy extends Component {
routes;
constructor(props) {
super(props);
this.routes = props.routes;
this.config = this.props.context;
this.state = {
data: {},
policyOverview: {},
policyId: '',
};
}
render() {
const {
match: { params },
} = this.props;
return (
<div>
<PageHeader style={{ paddingTop: 0 }}>
<Breadcrumb style={{ paddingBottom: 16 }}>
<Breadcrumb.Item>
<Link to="/entgra">
<Icon type="home" /> Home
</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>Policies</Breadcrumb.Item>
</Breadcrumb>
<div className="wrap">
{/* <h3>Policies</h3>*/}
{/* <Paragraph>Create new policy on IoT Server.</Paragraph>*/}
</div>
<div style={{ borderRadius: 5 }}>
<PolicyProfile policyId={params.policyId} />
</div>
</PageHeader>
<div
style={{ background: '#f0f2f5', padding: 24, minHeight: 720 }}
></div>
</div>
);
}
}
export default withConfigContext(ViewPolicy);

@ -49,6 +49,7 @@ import org.apache.axis2.transport.http.HTTPConstants;
import org.wso2.carbon.apimgt.annotations.api.Scope; import org.wso2.carbon.apimgt.annotations.api.Scope;
import org.wso2.carbon.apimgt.annotations.api.Scopes; import org.wso2.carbon.apimgt.annotations.api.Scopes;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.general.TenantDetail;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceGroupList;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
@ -100,6 +101,12 @@ import java.util.List;
description = "Permanently Delete the device specified by device id", description = "Permanently Delete the device specified by device id",
key = "perm:devices:permanent-delete", key = "perm:devices:permanent-delete",
permissions = {"/device-mgt/admin/devices/permanent-delete"} permissions = {"/device-mgt/admin/devices/permanent-delete"}
),
@Scope(
name = "Getting Details of Device tenants",
description = "Getting Details of Device tenants",
key = "perm:admin:tenant:view",
permissions = {"/device-mgt/devices/tenants/view"}
) )
} }
) )
@ -353,4 +360,54 @@ public interface DeviceManagementAdminService {
value = "List of device identifiers.", value = "List of device identifiers.",
required = true) required = true)
List<String> deviceIdentifiers); List<String> deviceIdentifiers);
@GET
@Path("/tenants")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Getting Details of tenants",
notes = "Get the details of tenants.",
response = TenantDetail.class,
responseContainer = "List",
tags = "Device Management Administrative Service",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value =
"perm:admin:tenant:view")
})
}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. \n Successfully fetched the list of tenants.",
response = TenantDetail.class,
responseContainer = "List",
responseHeaders = {
@ResponseHeader(
name = "Content-Type",
description = "The content type of the body"),
@ResponseHeader(
name = "ETag",
description = "Entity Tag of the response resource.\n" +
"Used by caches, or in conditional requests."),
@ResponseHeader(
name = "Last-Modified",
description = "Date and time the resource was last modified.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. Empty body because the client already has the latest version of the " +
"requested resource.\n"),
@ApiResponse(
code = 401,
message = "Unauthorized.\n The unauthorized access to the requested resource.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Server error occurred while fetching the" +
" tenant list.",
response = ErrorResponse.class)
})
Response getTenants();
} }

@ -45,13 +45,19 @@ import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException;
import org.wso2.carbon.device.mgt.common.general.TenantDetail;
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList;
import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.DeviceManagementAdminService; import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.DeviceManagementAdminService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import org.wso2.carbon.user.api.Tenant;
import org.wso2.carbon.user.api.TenantManager;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.GET; import javax.ws.rs.GET;
@ -62,6 +68,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam; import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List; import java.util.List;
@Path("/admin/devices") @Path("/admin/devices")
@ -189,4 +196,51 @@ public class DeviceManagementAdminServiceImpl implements DeviceManagementAdminSe
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
} }
} }
@Override
@Path("/tenants")
@GET
public Response getTenants() {
List<TenantDetail> tenantDetails;
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
if (tenantId == MultitenantConstants.SUPER_TENANT_ID) {
RealmService realmService = (RealmService) PrivilegedCarbonContext
.getThreadLocalCarbonContext().getOSGiService(RealmService.class, null);
if (realmService == null) {
String msg = "RealmService is not initialized";
log.error(msg);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
}
try {
Tenant[] tenants = realmService.getTenantManager().getAllTenants();
tenantDetails = new ArrayList<>();
if (tenants != null && tenants.length > 0) {
for (Tenant tenant : tenants) {
TenantDetail tenantDetail = new TenantDetail();
tenantDetail.setId(tenant.getId());
tenantDetail.setAdminFirstName(tenant.getAdminFirstName());
tenantDetail.setAdminFullName(tenant.getAdminFullName());
tenantDetail.setAdminLastName(tenant.getAdminLastName());
tenantDetail.setAdminName(tenant.getAdminName());
tenantDetail.setDomain(tenant.getDomain());
tenantDetail.setEmail(tenant.getEmail());
tenantDetails.add(tenantDetail);
}
return Response.status(Response.Status.OK).entity(tenantDetails).build();
} else {
return Response.status(Response.Status.NOT_FOUND).entity("No tenants found")
.build();
}
} catch (UserStoreException e) {
String msg = "Error occurred while fetching tenant list";
log.error(msg, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
}
} else {
return Response.status(Response.Status.BAD_REQUEST).entity("This API is available " +
"for super tenant admin only.").build();
}
}
} }

@ -53,6 +53,10 @@ public class Application implements Serializable {
private int memoryUsage; private int memoryUsage;
@ApiModelProperty(name = "isActive", value = "Is the application actively running", required = true) @ApiModelProperty(name = "isActive", value = "Is the application actively running", required = true)
private boolean isActive; private boolean isActive;
@ApiModelProperty(name = "hourlyUsage", value = "App hourly usage")
private long hourlyUsage;
@ApiModelProperty(name = "dailyUsage", value = "App daily usage")
private long dailyUsage;
public String getType() { public String getType() {
@ -179,4 +183,20 @@ public class Application implements Serializable {
public void setActive(boolean active) { public void setActive(boolean active) {
isActive = active; isActive = active;
} }
public long getHourlyUsage() {
return hourlyUsage;
}
public void setHourlyUsage(long hourlyUsage) {
this.hourlyUsage = hourlyUsage;
}
public long getDailyUsage() {
return dailyUsage;
}
public void setDailyUsage(long dailyUsage) {
this.dailyUsage = dailyUsage;
}
} }

@ -20,6 +20,7 @@ package org.wso2.carbon.device.mgt.common.device.details;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.app.mgt.Application;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import java.util.List; import java.util.List;
@ -31,6 +32,25 @@ public class DeviceDetailsWrapper {
DeviceLocation location; DeviceLocation location;
int tenantId; int tenantId;
List<DeviceGroup> groups;
String [] role;
public List<DeviceGroup> getGroups() {
return groups;
}
public void setGroups(List<DeviceGroup> groups) {
this.groups = groups;
}
public String [] getRole() {
return role;
}
public void setRole(String [] role) {
this.role = role;
}
public int getTenantId() { public int getTenantId() {
return tenantId; return tenantId;
} }

@ -0,0 +1,88 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (https://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.
*/
package org.wso2.carbon.device.mgt.common.general;
public class TenantDetail {
private int id;
private String domain;
private String adminName;
private String adminFullName;
private String adminFirstName;
private String adminLastName;
private String email;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getDomain() {
return domain;
}
public void setDomain(String domain) {
this.domain = domain;
}
public String getAdminName() {
return adminName;
}
public void setAdminName(String adminName) {
this.adminName = adminName;
}
public String getAdminFullName() {
return adminFullName;
}
public void setAdminFullName(String adminFullName) {
this.adminFullName = adminFullName;
}
public String getAdminFirstName() {
return adminFirstName;
}
public void setAdminFirstName(String adminFirstName) {
this.adminFirstName = adminFirstName;
}
public String getAdminLastName() {
return adminLastName;
}
public void setAdminLastName(String adminLastName) {
this.adminLastName = adminLastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}

@ -144,8 +144,9 @@ public final class DeviceManagementConstants {
private Report() { private Report() {
throw new AssertionError(); throw new AssertionError();
} }
public static final String REPORTING_EVENT_HOST = "iot.reporting.event.host";
public static final String REPORTING_CONTEXT = "/event"; public static final String REPORTING_CONTEXT = "/event";
public static final String DEVICE_INFO_ENDPOINT = REPORTING_CONTEXT + "/device-info"; public static final String DEVICE_INFO_ENDPOINT = REPORTING_CONTEXT + "/device-info";
public static final String REPORTING_EVENT_HOST = "iot.reporting.event.host"; public static final String APP_USAGE_ENDPOINT = REPORTING_CONTEXT + "/app-usage";
} }
} }

@ -25,6 +25,7 @@ import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.device.details.DeviceDetailsWrapper;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
@ -39,6 +40,7 @@ import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.util.HttpReportingUtil;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
@ -277,6 +279,17 @@ public class ApplicationManagerProviderServiceImpl implements ApplicationManagem
device.getEnrolmentInfo().getId(), tenantId); device.getEnrolmentInfo().getId(), tenantId);
} }
DeviceManagementDAOFactory.commitTransaction(); DeviceManagementDAOFactory.commitTransaction();
String reportingHost = HttpReportingUtil.getReportingHost();
if (!StringUtils.isBlank(reportingHost)) {
DeviceDetailsWrapper deviceDetailsWrapper = new DeviceDetailsWrapper();
deviceDetailsWrapper.setTenantId(tenantId);
deviceDetailsWrapper.setDevice(device);
deviceDetailsWrapper.setApplications(newApplications);
HttpReportingUtil.invokeApi(deviceDetailsWrapper.getJSONString(),
reportingHost + DeviceManagementConstants.Report.APP_USAGE_ENDPOINT);
}
} catch (DeviceManagementDAOException e) { } catch (DeviceManagementDAOException e) {
DeviceManagementDAOFactory.rollbackTransaction(); DeviceManagementDAOFactory.rollbackTransaction();
String msg = "Error occurred saving application list of the device " + device.getDeviceIdentifier(); String msg = "Error occurred saving application list of the device " + device.getDeviceIdentifier();

@ -22,16 +22,21 @@ import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.device.mgt.analytics.data.publisher.exception.DataPublisherConfigurationException; import org.wso2.carbon.device.mgt.analytics.data.publisher.exception.DataPublisherConfigurationException;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService;
import org.wso2.carbon.device.mgt.common.device.details.DeviceDetailsWrapper; import org.wso2.carbon.device.mgt.common.device.details.DeviceDetailsWrapper;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException; import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants;
import org.wso2.carbon.device.mgt.core.config.tenant.PlatformConfigurationManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
@ -40,12 +45,18 @@ import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManag
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO;
import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService;
import org.wso2.carbon.device.mgt.core.report.mgt.Constants; import org.wso2.carbon.device.mgt.core.report.mgt.Constants;
import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService;
import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil; import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil;
import org.wso2.carbon.device.mgt.core.util.HttpReportingUtil; import org.wso2.carbon.device.mgt.core.util.HttpReportingUtil;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -58,6 +69,7 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
private static final Log log = LogFactory.getLog(DeviceInformationManagerImpl.class); private static final Log log = LogFactory.getLog(DeviceInformationManagerImpl.class);
private static final String LOCATION_EVENT_STREAM_DEFINITION = "org.wso2.iot.LocationStream"; private static final String LOCATION_EVENT_STREAM_DEFINITION = "org.wso2.iot.LocationStream";
private static final String DEVICE_INFO_EVENT_STREAM_DEFINITION = "org.wso2.iot.DeviceInfoStream"; private static final String DEVICE_INFO_EVENT_STREAM_DEFINITION = "org.wso2.iot.DeviceInfoStream";
private static final String IS_EVENT_PUBLISHING_ENABED = "isEventPublishingEnabled";
public DeviceInformationManagerImpl() { public DeviceInformationManagerImpl() {
this.deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); this.deviceDAO = DeviceManagementDAOFactory.getDeviceDAO();
@ -79,6 +91,8 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
@Override @Override
public void addDeviceInfo(Device device, DeviceInfo deviceInfo) throws DeviceDetailsMgtException { public void addDeviceInfo(Device device, DeviceInfo deviceInfo) throws DeviceDetailsMgtException {
try { try {
publishEvents(device, deviceInfo);
DeviceManagementDAOFactory.beginTransaction(); DeviceManagementDAOFactory.beginTransaction();
DeviceInfo newDeviceInfo; DeviceInfo newDeviceInfo;
DeviceInfo previousDeviceInfo = deviceDetailsDAO.getDeviceInformation(device.getId(), DeviceInfo previousDeviceInfo = deviceDetailsDAO.getDeviceInformation(device.getId(),
@ -117,18 +131,6 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
deviceDAO.updateDevice(device, CarbonContext.getThreadLocalCarbonContext().getTenantId()); deviceDAO.updateDevice(device, CarbonContext.getThreadLocalCarbonContext().getTenantId());
DeviceManagementDAOFactory.commitTransaction(); DeviceManagementDAOFactory.commitTransaction();
String reportingHost = System.getProperty(DeviceManagementConstants.Report
.REPORTING_EVENT_HOST);
if (reportingHost != null && !reportingHost.isEmpty()) {
DeviceDetailsWrapper deviceDetailsWrapper = new DeviceDetailsWrapper();
deviceDetailsWrapper.setDevice(device);
deviceDetailsWrapper.setDeviceInfo(deviceInfo);
deviceDetailsWrapper.getJSONString();
HttpReportingUtil.invokeApi(deviceDetailsWrapper.getJSONString(),
reportingHost + DeviceManagementConstants.Report.DEVICE_INFO_ENDPOINT);
}
//TODO :: This has to be fixed by adding the enrollment ID. //TODO :: This has to be fixed by adding the enrollment ID.
if (DeviceManagerUtil.isPublishDeviceInfoResponseEnabled()) { if (DeviceManagerUtil.isPublishDeviceInfoResponseEnabled()) {
Object[] metaData = {device.getDeviceIdentifier(), device.getType()}; Object[] metaData = {device.getDeviceIdentifier(), device.getType()};
@ -170,18 +172,62 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
} catch (DeviceManagementDAOException e) { } catch (DeviceManagementDAOException e) {
DeviceManagementDAOFactory.rollbackTransaction(); DeviceManagementDAOFactory.rollbackTransaction();
throw new DeviceDetailsMgtException("Error occurred while updating the last update timestamp of the " + throw new DeviceDetailsMgtException("Error occurred while updating the last update timestamp of the " +
"device", e); "device", e);
} catch (DataPublisherConfigurationException e) { } catch (DataPublisherConfigurationException e) {
DeviceManagementDAOFactory.rollbackTransaction(); DeviceManagementDAOFactory.rollbackTransaction();
throw new DeviceDetailsMgtException("Error occurred while publishing the device location information.", e); throw new DeviceDetailsMgtException("Error occurred while publishing the device location information.", e);
} catch (EventPublishingException e) {
DeviceManagementDAOFactory.rollbackTransaction();
throw new DeviceDetailsMgtException("Error occurred while sending events", e);
} finally { } finally {
DeviceManagementDAOFactory.closeConnection(); DeviceManagementDAOFactory.closeConnection();
} }
} }
private void publishEvents(Device device, DeviceInfo deviceInfo) {
String reportingHost = HttpReportingUtil.getReportingHost();
if (!StringUtils.isBlank(reportingHost) && isPublishingEnabledForTenant()) {
try {
DeviceDetailsWrapper deviceDetailsWrapper = new DeviceDetailsWrapper();
deviceDetailsWrapper.setDevice(device);
deviceDetailsWrapper.setDeviceInfo(deviceInfo);
deviceDetailsWrapper.setTenantId(DeviceManagerUtil.getTenantId());
GroupManagementProviderService groupManagementService = DeviceManagementDataHolder
.getInstance().getGroupManagementProviderService();
List<DeviceGroup> groups = groupManagementService.getGroups(device, false);
if (groups != null && groups.size() > 0) {
deviceDetailsWrapper.setGroups(groups);
}
String[] rolesOfUser = getRolesOfUser(CarbonContext.getThreadLocalCarbonContext()
.getUsername());
if (rolesOfUser != null && rolesOfUser.length > 0) {
deviceDetailsWrapper.setRole(rolesOfUser);
}
HttpReportingUtil.invokeApi(deviceDetailsWrapper.getJSONString(),
reportingHost + DeviceManagementConstants.Report.DEVICE_INFO_ENDPOINT);
} catch (EventPublishingException e) {
log.error("Error occurred while sending events", e);
} catch (GroupManagementException e) {
log.error("Error occurred while getting group list", e);
} catch (UserStoreException e) {
log.error("Error occurred while getting role list", e);
}
} else {
if(log.isTraceEnabled()) {
log.trace("Event publishing is not enabled for tenant "
+ DeviceManagerUtil.getTenantId());
}
}
}
private boolean isPublishingEnabledForTenant() {
Object configuration = DeviceManagerUtil.getConfiguration(IS_EVENT_PUBLISHING_ENABED);
if (configuration != null) {
return Boolean.valueOf(configuration.toString());
}
return false;
}
@Override @Override
public DeviceInfo getDeviceInfo(DeviceIdentifier deviceId) throws DeviceDetailsMgtException { public DeviceInfo getDeviceInfo(DeviceIdentifier deviceId) throws DeviceDetailsMgtException {
Device device = getDevice(deviceId); Device device = getDevice(deviceId);
@ -427,6 +473,7 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
return newDeviceInfo; return newDeviceInfo;
} }
/** /**
* Generate and add a value depending on the device's OS version included in device info * Generate and add a value depending on the device's OS version included in device info
* *
@ -461,5 +508,20 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
} }
} }
} }
private String[] getRolesOfUser(String userName) throws UserStoreException {
UserRealm userRealm = CarbonContext.getThreadLocalCarbonContext().getUserRealm();
String[] roleList;
if (userRealm != null) {
userRealm.getUserStoreManager().getRoleNames();
roleList = userRealm.getUserStoreManager().getRoleListOfUser(userName);
} else {
String msg = "User realm is not initiated. Logged in user: " + userName;
log.error(msg);
throw new UserStoreException(msg);
}
return roleList;
}
} }

@ -243,6 +243,16 @@ public interface GroupManagementProviderService {
*/ */
List<DeviceGroup> getGroups(DeviceIdentifier deviceIdentifier, boolean requireGroupProps) throws GroupManagementException; List<DeviceGroup> getGroups(DeviceIdentifier deviceIdentifier, boolean requireGroupProps) throws GroupManagementException;
/**
* Get groups which contains particular device.
*
* @param device interested devoce.
* @return groups contain the device.
* @throws GroupManagementException
*/
public List<DeviceGroup> getGroups(Device device, boolean requireGroupProps)
throws GroupManagementException;
/** /**
* Checks for the default group existence and create group based on device ownership. * Checks for the default group existence and create group based on device ownership.
* @param groupName of the group * @param groupName of the group

@ -938,6 +938,42 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
} }
} }
@Override
public List<DeviceGroup> getGroups(Device device, boolean requireGroupProps)
throws GroupManagementException {
if (device.getDeviceIdentifier() == null) {
String msg = "Received empty device identifier for getGroups";
log.error(msg);
throw new GroupManagementException(msg);
}
if (log.isDebugEnabled()) {
log.debug("Get groups of device " + device.getDeviceIdentifier());
}
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
GroupManagementDAOFactory.openConnection();
List<DeviceGroup> deviceGroups = groupDAO.getGroups(device.getId(), tenantId);
if (requireGroupProps) {
if (deviceGroups != null && !deviceGroups.isEmpty()) {
for (DeviceGroup group : deviceGroups) {
populateGroupProperties(group, tenantId);
}
}
}
return deviceGroups;
} catch (GroupManagementDAOException | SQLException e) {
String msg = "Error occurred while retrieving device groups.";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (Exception e) {
String msg = "Error occurred in getGroups";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} finally {
GroupManagementDAOFactory.closeConnection();
}
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

@ -38,6 +38,10 @@ import org.wso2.carbon.device.mgt.common.ApplicationRegistration;
import org.wso2.carbon.device.mgt.common.ApplicationRegistrationException; import org.wso2.carbon.device.mgt.common.ApplicationRegistrationException;
import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry;
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest; import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
@ -53,6 +57,8 @@ import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager;
import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig;
import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig;
import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition; import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition;
import org.wso2.carbon.device.mgt.core.config.policy.PolicyConfiguration;
import org.wso2.carbon.device.mgt.core.config.tenant.PlatformConfigurationManagementServiceImpl;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO;
@ -97,9 +103,10 @@ import java.util.regex.Pattern;
import java.util.stream.IntStream; import java.util.stream.IntStream;
public final class DeviceManagerUtil { public final class DeviceManagerUtil {
private static final Log log = LogFactory.getLog(DeviceManagerUtil.class); private static final Log log = LogFactory.getLog(DeviceManagerUtil.class);
public static final String GENERAL_CONFIG_RESOURCE_PATH = "general";
private static boolean isDeviceCacheInitialized = false; private static boolean isDeviceCacheInitialized = false;
@ -348,6 +355,11 @@ public final class DeviceManagerUtil {
} }
} }
public static int getTenantId() {
return PrivilegedCarbonContext
.getThreadLocalCarbonContext().getTenantId();
}
public static int validateActivityListPageSize(int limit) throws OperationManagementException { public static int validateActivityListPageSize(int limit) throws OperationManagementException {
if (limit == 0) { if (limit == 0) {
DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance(). DeviceManagementConfig deviceManagementConfig = DeviceConfigurationManager.getInstance().
@ -804,4 +816,28 @@ public final class DeviceManagerUtil {
} }
return joiner.toString(); return joiner.toString();
} }
public static Object getConfiguration(String key) {
PlatformConfigurationManagementService configMgtService =
new PlatformConfigurationManagementServiceImpl();
try {
PlatformConfiguration tenantConfiguration = configMgtService.getConfiguration
(GENERAL_CONFIG_RESOURCE_PATH);
List<ConfigurationEntry> configuration = tenantConfiguration.getConfiguration();
if (configuration != null && !configuration.isEmpty()) {
for (ConfigurationEntry cEntry : configuration) {
if (key.equalsIgnoreCase(cEntry.getName())) {
return cEntry.getValue();
}
}
}
} catch (ConfigurationManagementException e) {
log.error("Error while getting the configurations from registry.", e);
return null;
}
return null;
}
} }

@ -25,11 +25,16 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HTTP;
import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException; import org.wso2.carbon.device.mgt.common.exceptions.EventPublishingException;
import org.wso2.carbon.device.mgt.core.DeviceManagementConstants;
import java.io.IOException; import java.io.IOException;
public class HttpReportingUtil { public class HttpReportingUtil {
public static String getReportingHost() {
return System.getProperty(DeviceManagementConstants.Report.REPORTING_EVENT_HOST);
}
public static int invokeApi(String payload, String endpoint) throws EventPublishingException { public static int invokeApi(String payload, String endpoint) throws EventPublishingException {
try (CloseableHttpClient client = HttpClients.createDefault()) { try (CloseableHttpClient client = HttpClients.createDefault()) {
HttpPost apiEndpoint = new HttpPost(endpoint); HttpPost apiEndpoint = new HttpPost(endpoint);

@ -25,6 +25,9 @@ public class SubPanel {
@XmlElement(name = "PanelKey", required = true) @XmlElement(name = "PanelKey", required = true)
protected String id; protected String id;
@XmlElement(name = "SubPanelValues")
private SubPanelValuesList others;
@XmlElementWrapper(name = "PanelItems") @XmlElementWrapper(name = "PanelItems")
@XmlElement(name = "PanelItem") @XmlElement(name = "PanelItem")
private List<PanelItem> panelItem; private List<PanelItem> panelItem;
@ -37,6 +40,14 @@ public class SubPanel {
this.id = id; this.id = id;
} }
public SubPanelValuesList getPayloadAttributes() {
return others;
}
public void setPayloadAttributes(SubPanelValuesList others) {
this.others = others;
}
public List<PanelItem> getPanelItemList1() { public List<PanelItem> getPanelItemList1() {
return panelItem; return panelItem;
} }

@ -0,0 +1,27 @@
package org.wso2.carbon.device.mgt.extensions.device.type.template.config;
import javax.xml.bind.annotation.XmlElement;
public class SubPanelValuesList {
@XmlElement(name = "PanelSwitch")
private String itemSwitch;
@XmlElement(name = "PayloadKey")
private String itemPayload;
public String getSwitchItem() {
return itemSwitch;
}
public void setSwitchItem(String itemSwitch) {
this.itemSwitch = itemSwitch;
}
public String getPayloadItem() {
return itemPayload;
}
public void setPayloadItem(String itemPayload) {
this.itemPayload = itemPayload;
}
}

@ -95,6 +95,21 @@
class="form-control" class="form-control"
placeholder="[ Required Field ]"> placeholder="[ Required Field ]">
</div> </div>
<div class="wr-input-control">
<label class="wr-input-label" for="publish-for-analytics">
Publish For Analytics
<span class="helper" title="Should publish events">
<span class="wr-help-tip glyphicon glyphicon-question-sign"></span>
</span>
</label>
<select id="publish-for-analytics"
class="form-control">
<option value="false" selected="selected">
false
</option>
<option value="true">true</option>
</select>
</div>
<div class="wr-input-control wr-btn-grp"> <div class="wr-input-control wr-btn-grp">
<button id="save-general-btn" class="wr-btn">Save</button> <button id="save-general-btn" class="wr-btn">Save</button>
</div> </div>

@ -22,7 +22,8 @@ $(document).ready(function () {
var configParams = { var configParams = {
"NOTIFIER_TYPE": "notifierType", "NOTIFIER_TYPE": "notifierType",
"NOTIFIER_FREQUENCY": "notifierFrequency" "NOTIFIER_FREQUENCY": "notifierFrequency",
"IS_EVENT_PUBLISHING_ENABLED": "isEventPublishingEnabled"
}; };
var responseCodes = { var responseCodes = {
@ -51,6 +52,8 @@ $(document).ready(function () {
var config = data.configuration[i]; var config = data.configuration[i];
if (config.name == configParams["NOTIFIER_FREQUENCY"]) { if (config.name == configParams["NOTIFIER_FREQUENCY"]) {
$("input#monitoring-config-frequency").val(config.value / 1000); $("input#monitoring-config-frequency").val(config.value / 1000);
} else if (config.name == configParams["IS_EVENT_PUBLISHING_ENABLED"]) {
$("select#publish-for-analytics").val(config.value);
} }
} }
} }
@ -65,6 +68,7 @@ $(document).ready(function () {
*/ */
$("button#save-general-btn").click(function () { $("button#save-general-btn").click(function () {
var notifierFrequency = $("input#monitoring-config-frequency").val(); var notifierFrequency = $("input#monitoring-config-frequency").val();
var publishEvents = $("select#publish-for-analytics").val();
var errorMsgWrapper = "#email-config-error-msg"; var errorMsgWrapper = "#email-config-error-msg";
var errorMsg = "#email-config-error-msg span"; var errorMsg = "#email-config-error-msg span";
@ -84,6 +88,13 @@ $(document).ready(function () {
"contentType": "text" "contentType": "text"
}; };
var publishEventsDetails = {
"name": configParams["IS_EVENT_PUBLISHING_ENABLED"],
"value": publishEvents,
"contentType": "text"
};
configList.push(publishEventsDetails);
configList.push(monitorFrequency); configList.push(monitorFrequency);
addConfigFormData.configuration = configList; addConfigFormData.configuration = configList;

@ -86,6 +86,7 @@
org.wso2.carbon.utils, org.wso2.carbon.utils,
org.wso2.carbon.utils.multitenancy, org.wso2.carbon.utils.multitenancy,
org.xml.sax, org.xml.sax,
com.google.gson.*,
javax.servlet, javax.servlet,
javax.servlet.http, javax.servlet.http,
javax.xml, javax.xml,
@ -215,6 +216,10 @@
<groupId>org.wso2.carbon.devicemgt</groupId> <groupId>org.wso2.carbon.devicemgt</groupId>
<artifactId>org.wso2.carbon.device.mgt.common</artifactId> <artifactId>org.wso2.carbon.device.mgt.common</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.wso2.orbit.org.apache.httpcomponents</groupId> <groupId>org.wso2.orbit.org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>

@ -22,6 +22,13 @@ import org.apache.catalina.connector.Response;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.wso2.carbon.base.MultitenantConstants;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import org.wso2.carbon.webapp.authenticator.framework.internal.AuthenticatorFrameworkDataHolder;
import javax.xml.XMLConstants; import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
@ -32,6 +39,7 @@ import java.io.IOException;
public class AuthenticationFrameworkUtil { public class AuthenticationFrameworkUtil {
private static final Log log = LogFactory.getLog(AuthenticationFrameworkUtil.class); private static final Log log = LogFactory.getLog(AuthenticationFrameworkUtil.class);
private static final String UI_EXECUTE = "ui.execute";
static void handleResponse(Request request, Response response, int statusCode, String payload) { static void handleResponse(Request request, Response response, int statusCode, String payload) {
response.setStatus(statusCode); response.setStatus(statusCode);
@ -65,4 +73,43 @@ public class AuthenticationFrameworkUtil {
} }
} }
static boolean isUserAuthorized(int tenantId, String tenantDomain, String username, String
permission) throws
AuthenticationException {
boolean tenantFlowStarted = false;
try{
//If this is a tenant user
if(tenantId != MultitenantConstants.SUPER_TENANT_ID){
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(tenantDomain);
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(tenantId);
PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername(username);
tenantFlowStarted = true;
}
RealmService realmService = AuthenticatorFrameworkDataHolder.getInstance().getRealmService();
if (realmService == null) {
String msg = "RealmService is not initialized";
log.error(msg);
throw new AuthenticationException(msg);
}
UserRealm userRealm = realmService.getTenantUserRealm(tenantId);
return userRealm.getAuthorizationManager()
.isUserAuthorized(MultitenantUtils
.getTenantAwareUsername(username), permission, UI_EXECUTE);
} catch (UserStoreException e) {
String msg = "Error while getting username";
log.error(msg, e);
throw new AuthenticationException(msg, e);
}
finally {
if (tenantFlowStarted) {
PrivilegedCarbonContext.endTenantFlow();
}
}
}
} }

@ -30,6 +30,7 @@ public class AuthenticationInfo {
private String username; private String username;
private String tenantDomain; private String tenantDomain;
private int tenantId = -1; private int tenantId = -1;
private boolean isSuperTenantAdmin;
public WebappAuthenticator.Status getStatus() { public WebappAuthenticator.Status getStatus() {
return status; return status;
@ -71,4 +72,12 @@ public class AuthenticationInfo {
public void setTenantId(int tenantId) { public void setTenantId(int tenantId) {
this.tenantId = tenantId; this.tenantId = tenantId;
} }
public boolean isSuperTenantAdmin() {
return isSuperTenantAdmin;
}
public void setSuperTenantAdmin(boolean superTenantAdmin) {
isSuperTenantAdmin = superTenantAdmin;
}
} }

@ -22,6 +22,7 @@ public final class Constants {
public static final String AUTHORIZATION_HEADER_PREFIX_BEARER = "Bearer"; public static final String AUTHORIZATION_HEADER_PREFIX_BEARER = "Bearer";
public static final String NO_MATCHING_AUTH_SCHEME = "noMatchedAuthScheme"; public static final String NO_MATCHING_AUTH_SCHEME = "noMatchedAuthScheme";
public static final String PROXY_TENANT_ID = "Proxy-Tenant-Id";
public static final class HTTPHeaders { public static final class HTTPHeaders {
private HTTPHeaders() { private HTTPHeaders() {

@ -18,15 +18,20 @@
*/ */
package org.wso2.carbon.webapp.authenticator.framework; package org.wso2.carbon.webapp.authenticator.framework;
import com.google.gson.Gson;
import org.apache.catalina.Context; import org.apache.catalina.Context;
import org.apache.catalina.connector.Request; import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response; import org.apache.catalina.connector.Response;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.owasp.encoder.Encode; import org.owasp.encoder.Encode;
import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve; import org.wso2.carbon.tomcat.ext.valves.CarbonTomcatValve;
import org.wso2.carbon.tomcat.ext.valves.CompositeValve; import org.wso2.carbon.tomcat.ext.valves.CompositeValve;
import org.wso2.carbon.user.api.Tenant;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator; import org.wso2.carbon.webapp.authenticator.framework.authenticator.WebappAuthenticator;
import org.wso2.carbon.webapp.authenticator.framework.authorizer.WebappTenantAuthorizer; import org.wso2.carbon.webapp.authenticator.framework.authorizer.WebappTenantAuthorizer;
@ -39,12 +44,15 @@ public class WebappAuthenticationValve extends CarbonTomcatValve {
private static final Log log = LogFactory.getLog(WebappAuthenticationValve.class); private static final Log log = LogFactory.getLog(WebappAuthenticationValve.class);
private static TreeMap<String, String> nonSecuredEndpoints = new TreeMap<>(); private static TreeMap<String, String> nonSecuredEndpoints = new TreeMap<>();
private static final String PERMISSION_PREFIX = "/permission/admin";
public static final String AUTHORIZE_PERMISSION = "Authorize-Permission";
@Override @Override
public void invoke(Request request, Response response, CompositeValve compositeValve) { public void invoke(Request request, Response response, CompositeValve compositeValve) {
if (this.isContextSkipped(request) || this.skipAuthentication(request)) { if ((this.isContextSkipped(request) || this.skipAuthentication(request))
this.getNext().invoke(request, response, compositeValve); && (StringUtils.isEmpty(request.getHeader(AUTHORIZE_PERMISSION)))) {
this.getNext().invoke(request, response, compositeValve);
return; return;
} }
@ -60,6 +68,41 @@ public class WebappAuthenticationValve extends CarbonTomcatValve {
WebappAuthenticator.Status status = WebappTenantAuthorizer.authorize(request, authenticationInfo); WebappAuthenticator.Status status = WebappTenantAuthorizer.authorize(request, authenticationInfo);
authenticationInfo.setStatus(status); authenticationInfo.setStatus(status);
} }
// This section will allow to validate a given access token is authenticated to access given
// resource(permission)
if (request.getCoyoteRequest() != null
&& StringUtils.isNotEmpty(request.getHeader(AUTHORIZE_PERMISSION))
&& (authenticationInfo.getStatus() == WebappAuthenticator.Status.CONTINUE ||
authenticationInfo.getStatus() == WebappAuthenticator.Status.SUCCESS)) {
boolean isAllowed;
try {
isAllowed = AuthenticationFrameworkUtil.isUserAuthorized(
authenticationInfo.getTenantId(), authenticationInfo.getTenantDomain(),
authenticationInfo.getUsername(),
PERMISSION_PREFIX + request.getHeader (AUTHORIZE_PERMISSION));
} catch (AuthenticationException e) {
String msg = "Could not authorize permission";
log.error(msg);
AuthenticationFrameworkUtil.handleResponse(request, response,
HttpServletResponse.SC_INTERNAL_SERVER_ERROR, msg);
return;
}
if (isAllowed) {
Gson gson = new Gson();
AuthenticationFrameworkUtil.handleResponse(request, response, HttpServletResponse.SC_OK,
gson.toJson(authenticationInfo));
return;
} else {
log.error("Unauthorized message from user " + authenticationInfo.getUsername());
AuthenticationFrameworkUtil.handleResponse(request, response,
HttpServletResponse.SC_FORBIDDEN, "Unauthorized to access the API");
return;
}
}
Tenant tenant = null;
if (authenticationInfo.getTenantId() != -1) { if (authenticationInfo.getTenantId() != -1) {
try { try {
PrivilegedCarbonContext.startTenantFlow(); PrivilegedCarbonContext.startTenantFlow();
@ -67,10 +110,49 @@ public class WebappAuthenticationValve extends CarbonTomcatValve {
privilegedCarbonContext.setTenantId(authenticationInfo.getTenantId()); privilegedCarbonContext.setTenantId(authenticationInfo.getTenantId());
privilegedCarbonContext.setTenantDomain(authenticationInfo.getTenantDomain()); privilegedCarbonContext.setTenantDomain(authenticationInfo.getTenantDomain());
privilegedCarbonContext.setUsername(authenticationInfo.getUsername()); privilegedCarbonContext.setUsername(authenticationInfo.getUsername());
this.processRequest(request, response, compositeValve, authenticationInfo); if (authenticationInfo.isSuperTenantAdmin() && request.getHeader(Constants
.PROXY_TENANT_ID) != null) {
// If this is a call from super admin to an API and the ProxyTenantId is also
// present, this is a call that is made with super admin credentials to call
// an API on behalf of another tenant. Hence the actual tenants, details are
// resolved instead of calling processRequest.
int tenantId = Integer.valueOf(request.getHeader(Constants.PROXY_TENANT_ID));
RealmService realmService = (RealmService) PrivilegedCarbonContext
.getThreadLocalCarbonContext().getOSGiService(RealmService.class, null);
if (realmService == null) {
String msg = "RealmService is not initialized";
log.error(msg);
AuthenticationFrameworkUtil.handleResponse(request, response,
HttpServletResponse.SC_BAD_REQUEST, msg);
return;
}
tenant = realmService.getTenantManager().getTenant(tenantId);
} else {
this.processRequest(request, response, compositeValve, authenticationInfo);
}
} catch (UserStoreException e) {
String msg = "Could not locate the tenant";
log.error(msg);
AuthenticationFrameworkUtil.handleResponse(request, response,
HttpServletResponse.SC_BAD_REQUEST, msg);
} finally { } finally {
PrivilegedCarbonContext.endTenantFlow(); PrivilegedCarbonContext.endTenantFlow();
} }
// A call from super admin to a child tenant. Start a new tenant flow of the target
// tenant and pass to the API.
if (tenant != null) {
try {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext privilegedCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
privilegedCarbonContext.setTenantId(tenant.getId());
privilegedCarbonContext.setTenantDomain(tenant.getDomain());
privilegedCarbonContext.setUsername(tenant.getAdminName());
this.processRequest(request, response, compositeValve, authenticationInfo);
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
} else { } else {
this.processRequest(request, response, compositeValve, authenticationInfo); this.processRequest(request, response, compositeValve, authenticationInfo);
} }

@ -25,6 +25,7 @@ import org.apache.tomcat.util.buf.ByteChunk;
import org.apache.tomcat.util.buf.MessageBytes; import org.apache.tomcat.util.buf.MessageBytes;
import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException; import org.wso2.carbon.webapp.authenticator.framework.AuthenticationException;
import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo; import org.wso2.carbon.webapp.authenticator.framework.AuthenticationInfo;
import org.wso2.carbon.webapp.authenticator.framework.Constants;
import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils; import org.wso2.carbon.webapp.authenticator.framework.Utils.Utils;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuth2TokenValidator; import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuth2TokenValidator;
import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuthTokenValidationException; import org.wso2.carbon.webapp.authenticator.framework.authenticator.oauth.OAuthTokenValidationException;
@ -77,6 +78,11 @@ public class OAuthAuthenticator implements WebappAuthenticator {
String resource = requestUri + ":" + requestMethod; String resource = requestUri + ":" + requestMethod;
OAuthValidationResponse oAuthValidationResponse = this.tokenValidator.validateToken(bearerToken, resource); OAuthValidationResponse oAuthValidationResponse = this.tokenValidator.validateToken(bearerToken, resource);
authenticationInfo = Utils.setAuthenticationInfo(oAuthValidationResponse, authenticationInfo); authenticationInfo = Utils.setAuthenticationInfo(oAuthValidationResponse, authenticationInfo);
if (authenticationInfo.getTenantId() == -1234 && properties.getProperty("Username")
.equals(authenticationInfo.getUsername())
&& request.getHeader(Constants.PROXY_TENANT_ID) != null) {
authenticationInfo.setSuperTenantAdmin(true);
}
} catch (AuthenticationException e) { } catch (AuthenticationException e) {
log.error("Failed to authenticate the incoming request", e); log.error("Failed to authenticate the incoming request", e);
} catch (OAuthTokenValidationException e) { } catch (OAuthTokenValidationException e) {

@ -55,8 +55,9 @@ public class WebappAuthenticationValveTest {
@Test(description = "This method tests the invoke method of the WebAppAuthenticationValve with the context path " @Test(description = "This method tests the invoke method of the WebAppAuthenticationValve with the context path "
+ "starting with carbon") + "starting with carbon")
public void testInvokeWithContextSkippedScenario1() { public void testInvokeWithContextSkippedScenario1() throws NoSuchFieldException, IllegalAccessException {
Request request = new Request(); Request request = new Request();
getCoyoteRequest(request);
Context context = new StandardContext(); Context context = new StandardContext();
context.setPath("carbon"); context.setPath("carbon");
CompositeValve compositeValve = Mockito.mock(CompositeValve.class); CompositeValve compositeValve = Mockito.mock(CompositeValve.class);
@ -64,6 +65,7 @@ public class WebappAuthenticationValveTest {
request.setContext(context); request.setContext(context);
webappAuthenticationValve.invoke(request, null, compositeValve); webappAuthenticationValve.invoke(request, null, compositeValve);
request = new TestRequest("", "test"); request = new TestRequest("", "test");
getCoyoteRequest(request);
context = new StandardContext(); context = new StandardContext();
compositeValve = Mockito.mock(CompositeValve.class); compositeValve = Mockito.mock(CompositeValve.class);
Mockito.doNothing().when(compositeValve).continueInvocation(Mockito.any(), Mockito.any()); Mockito.doNothing().when(compositeValve).continueInvocation(Mockito.any(), Mockito.any());
@ -73,8 +75,9 @@ public class WebappAuthenticationValveTest {
@Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when " @Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when "
+ "un-secured endpoints are invoked.") + "un-secured endpoints are invoked.")
public void testInvokeUnSecuredEndpoints() { public void testInvokeUnSecuredEndpoints() throws IllegalAccessException, NoSuchFieldException {
Request request = new TestRequest("", "test"); Request request = new TestRequest("", "test");
getCoyoteRequest(request);
Context context = new StandardContext(); Context context = new StandardContext();
context.setPath("carbon1"); context.setPath("carbon1");
context.addParameter("doAuthentication", String.valueOf(true)); context.addParameter("doAuthentication", String.valueOf(true));
@ -85,6 +88,22 @@ public class WebappAuthenticationValveTest {
webappAuthenticationValve.invoke(request, null, compositeValve); webappAuthenticationValve.invoke(request, null, compositeValve);
} }
private void getCoyoteRequest(Request request) throws
IllegalAccessException,
NoSuchFieldException {
Field headersField = org.apache.coyote.Request.class.getDeclaredField("headers");
headersField.setAccessible(true);
org.apache.coyote.Request coyoteRequest = new org.apache.coyote.Request();
MimeHeaders mimeHeaders = new MimeHeaders();
MessageBytes bytes = mimeHeaders.addValue("content-type");
bytes.setString("test");
headersField.set(coyoteRequest, mimeHeaders);
request.setCoyoteRequest(coyoteRequest);
}
@Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when " @Test(description = "This method tests the behaviour of the invoke method of WebAuthenticationValve when "
+ "secured endpoints are invoked.") + "secured endpoints are invoked.")
public void testInvokeSecuredEndpoints() throws NoSuchFieldException, IllegalAccessException { public void testInvokeSecuredEndpoints() throws NoSuchFieldException, IllegalAccessException {

Loading…
Cancel
Save