diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js
index 4f0862d4276..8d3539854a4 100644
--- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js
+++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/index.js
@@ -40,7 +40,7 @@ import Policies from './scenes/Home/scenes/Policies';
import AddNewPolicy from './scenes/Home/scenes/Policies/scenes/AddNewPolicy';
import Roles from './scenes/Home/scenes/Roles';
import DeviceTypes from './scenes/Home/scenes/DeviceTypes';
-import Certificates from './scenes/Home/scenes/Certificates';
+import Certificates from './scenes/Home/scenes/Configurations/scenes/Certificates';
import Devices from './scenes/Home/scenes/Devices';
const routes = [
@@ -105,7 +105,7 @@ const routes = [
exact: true,
},
{
- path: '/entgra/certificates',
+ path: '/entgra/configurations/certificates',
component: Certificates,
exact: true,
},
diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/index.js
index 6a8fc986987..ab941cfb232 100644
--- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/index.js
+++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/index.js
@@ -151,7 +151,7 @@ class Home extends React.Component {
}
>
-
+
Certificates
diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Certificates/components/CertificateTable/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Configurations/scenes/Certificates/components/CertificateTable/index.js
similarity index 98%
rename from components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Certificates/components/CertificateTable/index.js
rename to components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Configurations/scenes/Certificates/components/CertificateTable/index.js
index f59fb81ffa8..cafeaaca992 100644
--- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Certificates/components/CertificateTable/index.js
+++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Configurations/scenes/Certificates/components/CertificateTable/index.js
@@ -30,7 +30,7 @@ import {
import TimeAgo from 'javascript-time-ago';
// Load locale-specific relative date/time formatting rules.
import en from 'javascript-time-ago/locale/en';
-import { withConfigContext } from '../../../../../../components/ConfigContext';
+import { withConfigContext } from '../../../../../../../../components/ConfigContext';
import Moment from 'react-moment';
const { Paragraph, Text } = Typography;
diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Certificates/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Configurations/scenes/Certificates/index.js
similarity index 100%
rename from components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Certificates/index.js
rename to components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Configurations/scenes/Certificates/index.js
diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/components/ExternalDevicesModal/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/components/ExternalDevicesModal/index.js
new file mode 100644
index 00000000000..c1c7d1878c1
--- /dev/null
+++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/components/ExternalDevicesModal/index.js
@@ -0,0 +1,221 @@
+/*
+ * 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, Form, Input, Modal, notification, Col, Row } from 'antd';
+import axios from 'axios';
+import { withConfigContext } from '../../../../../../../../components/ConfigContext';
+import { handleApiError } from '../../../../../../../../services/utils/errorHandler';
+const InputGroup = Input.Group;
+
+class ExternalDevicesModal extends React.Component {
+ constructor(props) {
+ super(props);
+ this.config = this.props.context;
+ this.state = {
+ isDeviceEditModalVisible: false,
+ metaData: [],
+ };
+ }
+
+ openDeviceEditModal = () => {
+ this.setState({
+ isDeviceEditModalVisible: true,
+ });
+ this.getExternalDevicesForUser(this.props.user);
+ };
+
+ onCancelHandler = () => {
+ this.setState({
+ isDeviceEditModalVisible: false,
+ });
+ };
+
+ getExternalDevicesForUser = userName => {
+ let apiURL =
+ window.location.origin +
+ this.config.serverConfig.invoker.uri +
+ this.config.serverConfig.invoker.deviceMgt +
+ `/users/claims/${userName}`;
+
+ axios
+ .get(apiURL)
+ .then(res => {
+ if (res.status === 200) {
+ if (res.data.data.hasOwnProperty('http://wso2.org/claims/devices')) {
+ this.setState({
+ metaData: JSON.parse(
+ res.data.data['http://wso2.org/claims/devices'],
+ ),
+ });
+ }
+ }
+ })
+ .catch(error => {
+ handleApiError(
+ error,
+ 'Error occurred while trying to retrieve claims.',
+ );
+ });
+ };
+
+ setExternalDevicesForUser = (userName, payload) => {
+ let apiURL =
+ window.location.origin +
+ this.config.serverConfig.invoker.uri +
+ this.config.serverConfig.invoker.deviceMgt +
+ `/users/claims/${userName}`;
+
+ axios
+ .put(apiURL, payload)
+ .then(res => {
+ if (res.status === 200) {
+ notification.success({
+ message: 'Done',
+ duration: 0,
+ description: 'Succussfully updated.',
+ });
+ }
+ this.setState({
+ isDeviceEditModalVisible: false,
+ });
+ })
+ .catch(error => {
+ handleApiError(error, 'Error occurred while trying to update claims.');
+ });
+ };
+
+ onSubmitClaims = e => {
+ this.props.form.validateFields(['meta'], (err, values) => {
+ if (!err) {
+ this.setExternalDevicesForUser(this.props.user, this.state.metaData);
+ }
+ });
+ };
+
+ addNewMetaData = () => {
+ this.setState({
+ metaData: [...this.state.metaData, { deviceName: '', id: '' }],
+ });
+ };
+
+ render() {
+ const { getFieldDecorator } = this.props.form;
+ const { metaData } = this.state;
+ return (
+
+
+
+
+
+
+ Cancel
+ ,
+ ,
+ ]}
+ >
+
+
+
+
+ );
+ }
+}
+
+export default withConfigContext(
+ Form.create({ name: 'external-device-modal' })(ExternalDevicesModal),
+);
diff --git a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/index.js b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/index.js
index c97f5897b82..c2296c3d6fe 100644
--- a/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/index.js
+++ b/components/device-mgt/io.entgra.device.mgt.ui/react-app/src/scenes/Home/scenes/Users/components/UsersTable/index.js
@@ -27,6 +27,7 @@ import UsersDevices from './components/UserDevices';
import AddUser from './components/AddUser';
import UserActions from './components/UserActions';
import Filter from '../../../../components/Filter';
+import ExternalDevicesModal from './components/ExternalDevicesModal';
const ButtonGroup = Button.Group;
let apiUrl;
@@ -114,8 +115,6 @@ class UsersTable extends React.Component {
data: res.data.data.users,
pagination,
});
-
- console.log(res.data.data);
}
})
.catch(error => {
@@ -224,6 +223,7 @@ class UsersTable extends React.Component {
title: 'First Name',
dataIndex: 'firstname',
key: 'firstname',
+ width: 200,
},
{
title: 'Last Name',
@@ -260,6 +260,12 @@ class UsersTable extends React.Component {
),
},
+ {
+ title: 'External Device Claims',
+ dataIndex: 'claims',
+ key: 'claims',
+ render: (id, row) => ,
+ },
{
title: 'Action',
dataIndex: 'id',
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java
index d50f4cb50b3..9faf6d7ac2e 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/UserManagementService.java
@@ -34,6 +34,8 @@
*/
package org.wso2.carbon.device.mgt.jaxrs.service.api;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Info;
import io.swagger.annotations.ExtensionProperty;
@@ -1009,4 +1011,161 @@ public interface UserManagementService {
"Provide the value in the following format: EEE, d MMM yyyy HH:mm:ss Z\n." +
"Example: Mon, 05 Jan 2014 15:10:00 +0200")
@HeaderParam("If-Modified-Since") String ifModifiedSince);
+
+ @PUT
+ @Path("/claims/{username}")
+ @ApiOperation(
+ produces = MediaType.APPLICATION_JSON,
+ httpMethod = "PUT",
+ value = "Updating external device claims of user",
+ notes = "Update external device claims of a user registered with Entgra IoTS using the REST API.",
+ response = BasicUserInfo.class,
+ tags = "User Management",
+ extensions = {
+ @Extension(properties = {
+ @ExtensionProperty(name = Constants.SCOPE, value = "perm:users:details")
+ })
+ }
+ )
+ @ApiResponses(value = {
+ @ApiResponse(
+ code = 200,
+ message = "OK. \n Successfully updated external device claims of user.",
+ response = BasicUserInfo.class,
+ 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 = 404,
+ message = "Not Found. \n The specified resource does not exist.",
+ response = ErrorResponse.class),
+ @ApiResponse(
+ code = 500,
+ message = "Internal Server ErrorResponse. \n Server error occurred while" +
+ " fetching the user details.",
+ response = ErrorResponse.class)
+ })
+ Response updateUserClaimsForDevices(
+ @ApiParam(
+ name = "username",
+ value = "Provide the username of the user.",
+ required = true,
+ defaultValue = "admin")
+ @PathParam("username") String username,
+ @ApiParam(
+ name = "device list",
+ value = "Array of objects with device details",
+ required = true) JsonArray deviceList);
+
+ @GET
+ @Path("/claims/{username}")
+ @ApiOperation(
+ produces = MediaType.APPLICATION_JSON,
+ httpMethod = "GET",
+ value = "Getting external device claims of user",
+ notes = "Get external device claims of a user registered with Entgra IoTS using the REST API.",
+ response = BasicUserInfo.class,
+ tags = "User Management",
+ extensions = {
+ @Extension(properties = {
+ @ExtensionProperty(name = Constants.SCOPE, value = "perm:users:details")
+ })
+ }
+ )
+ @ApiResponses(value = {
+ @ApiResponse(
+ code = 200,
+ message = "OK. \n Successfully fetched external device claims of user.",
+ response = BasicUserInfo.class,
+ 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 = 404,
+ message = "Not Found. \n The specified resource does not exist.",
+ response = ErrorResponse.class),
+ @ApiResponse(
+ code = 500,
+ message = "Internal Server ErrorResponse. \n Server error occurred while" +
+ " fetching the ruser details.",
+ response = ErrorResponse.class)
+ })
+ Response getUserClaimsForDevices(
+ @ApiParam(
+ name = "username",
+ value = "Provide the username of the user.",
+ required = true,
+ defaultValue = "admin")
+ @PathParam("username") String username);
+
+ @DELETE
+ @Path("/claims/{username}")
+ @ApiOperation(
+ produces = MediaType.APPLICATION_JSON,
+ httpMethod = "DELETE",
+ value = "Deleting external device claims of user",
+ notes = "Delete external device claims of user registered with Entgra IoTS using the REST API.",
+ response = BasicUserInfo.class,
+ tags = "User Management",
+ extensions = {
+ @Extension(properties = {
+ @ExtensionProperty(name = Constants.SCOPE, value = "perm:users:details")
+ })
+ }
+ )
+ @ApiResponses(value = {
+ @ApiResponse(
+ code = 200,
+ message = "OK. \n Successfully deleted external device claims of user.",
+ response = BasicUserInfo.class,
+ 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 = 404,
+ message = "Not Found. \n The specified resource does not exist.",
+ response = ErrorResponse.class),
+ @ApiResponse(
+ code = 500,
+ message = "Internal Server ErrorResponse. \n Server error occurred while" +
+ " fetching the ruser details.",
+ response = ErrorResponse.class)
+ })
+ Response deleteUserClaimsForDevices(
+ @ApiParam(
+ name = "username",
+ value = "Provide the username of the user.",
+ required = true,
+ defaultValue = "admin")
+ @PathParam("username") String username);
}
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java
index 251aa0eb64e..a059ce3c474 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/UserManagementServiceImpl.java
@@ -33,6 +33,7 @@
*/
package org.wso2.carbon.device.mgt.jaxrs.service.impl;
+import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
@@ -71,6 +72,7 @@ import org.wso2.carbon.user.api.RealmConfiguration;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
+import org.wso2.carbon.user.core.UserCoreConstants;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.utils.CarbonUtils;
import org.wso2.carbon.utils.multitenancy.MultitenantConstants;
@@ -920,6 +922,113 @@ public class UserManagementServiceImpl implements UserManagementService {
}
}
+ @PUT
+ @Override
+ @Path("/claims/{username}")
+ public Response updateUserClaimsForDevices(
+ @PathParam("username") String username,
+ JsonArray deviceList) {
+ try {
+ RealmConfiguration realmConfiguration = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+ .getUserRealm()
+ .getRealmConfiguration();
+ String domain = realmConfiguration
+ .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
+ if (!StringUtils.isBlank(domain)) {
+ username = domain + Constants.FORWARD_SLASH + username;
+ }
+ UserStoreManager userStoreManager = DeviceMgtAPIUtils.getUserStoreManager();
+ if (!userStoreManager.isExistingUser(username)) {
+ if (log.isDebugEnabled()) {
+ log.debug("User by username: " + username + " does not exist.");
+ }
+ return Response.status(Response.Status.NOT_FOUND).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(
+ "User doesn't exist.").build()).build();
+ }
+ Map userClaims =
+ this.buildExternalDevicesUserClaims(username, domain, deviceList, userStoreManager);
+ userStoreManager.setUserClaimValues(username, userClaims, domain);
+ return Response.status(Response.Status.OK).entity(userClaims).build();
+ } catch (UserStoreException e) {
+ String msg = "Error occurred while updating external device claims of the user '" + username + "'";
+ log.error(msg, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
+ }
+ }
+
+ @GET
+ @Override
+ @Path("/claims/{username}")
+ public Response getUserClaimsForDevices(
+ @PathParam("username") String username) {
+ try {
+ RealmConfiguration realmConfiguration = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+ .getUserRealm()
+ .getRealmConfiguration();
+ String domain = realmConfiguration
+ .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
+ if (!StringUtils.isBlank(domain)) {
+ username = domain + Constants.FORWARD_SLASH + username;
+ }
+ UserStoreManager userStoreManager = DeviceMgtAPIUtils.getUserStoreManager();
+ if (!userStoreManager.isExistingUser(username)) {
+ if (log.isDebugEnabled()) {
+ log.debug("User by username: " + username + " does not exist.");
+ }
+ return Response.status(Response.Status.NOT_FOUND).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(
+ "User doesn't exist.").build()).build();
+ }
+ String[] claimArray = {Constants.USER_CLAIM_DEVICES};
+ Map claims = userStoreManager.getUserClaimValues(username, claimArray, domain);
+ return Response.status(Response.Status.OK).entity(claims).build();
+ } catch (UserStoreException e) {
+ String msg = "Error occurred while retrieving external device claims of the user '" + username + "'";
+ log.error(msg, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
+ }
+ }
+
+ @DELETE
+ @Override
+ @Path("/claims/{username}")
+ public Response deleteUserClaimsForDevices(
+ @PathParam("username") String username) {
+ try {
+ RealmConfiguration realmConfiguration = PrivilegedCarbonContext.getThreadLocalCarbonContext()
+ .getUserRealm()
+ .getRealmConfiguration();
+ String domain = realmConfiguration
+ .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_DOMAIN_NAME);
+ if (!StringUtils.isBlank(domain)) {
+ username = domain + Constants.FORWARD_SLASH + username;
+ }
+ UserStoreManager userStoreManager = DeviceMgtAPIUtils.getUserStoreManager();
+ if (!userStoreManager.isExistingUser(username)) {
+ if (log.isDebugEnabled()) {
+ log.debug("User by username: " + username + " does not exist.");
+ }
+ return Response.status(Response.Status.NOT_FOUND).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(
+ "User doesn't exist.").build()).build();
+ }
+ String[] claimArray = {Constants.USER_CLAIM_DEVICES};
+ userStoreManager.deleteUserClaimValues(
+ username,
+ claimArray,
+ domain);
+ return Response.status(Response.Status.OK).entity(claimArray).build();
+ } catch (UserStoreException e) {
+ String msg = "Error occurred while deleting external device claims of the user '" + username + "'";
+ log.error(msg, e);
+ return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
+ new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build();
+ }
+ }
+
private Map buildDefaultUserClaims(String firstName, String lastName, String emailAddress,
boolean isFresh) {
Map defaultUserClaims = new HashMap<>();
@@ -937,6 +1046,40 @@ public class UserManagementServiceImpl implements UserManagementService {
return defaultUserClaims;
}
+ /**
+ * This method is used to build String map for user claims with updated external device details
+ *
+ * @param username username of the particular user
+ * @param domain domain of the particular user
+ * @param deviceList Array of external device details
+ * @param userStoreManager {@link UserStoreManager} instance
+ * @return String map
+ * @throws UserStoreException If any error occurs while calling into UserStoreManager service
+ */
+ private Map buildExternalDevicesUserClaims(
+ String username,
+ String domain,
+ JsonArray deviceList,
+ UserStoreManager userStoreManager) throws UserStoreException {
+ Map userClaims;
+ String[] claimArray = {
+ Constants.USER_CLAIM_FIRST_NAME,
+ Constants.USER_CLAIM_LAST_NAME,
+ Constants.USER_CLAIM_EMAIL_ADDRESS,
+ Constants.USER_CLAIM_MODIFIED
+ };
+ userClaims = userStoreManager.getUserClaimValues(username, claimArray, domain);
+ if (userClaims.containsKey(Constants.USER_CLAIM_DEVICES)) {
+ userClaims.replace(Constants.USER_CLAIM_DEVICES, deviceList.toString());
+ } else {
+ userClaims.put(Constants.USER_CLAIM_DEVICES, deviceList.toString());
+ }
+ if (log.isDebugEnabled()) {
+ log.debug("Claim map is created for user: " + username + ", claims:" + userClaims.toString());
+ }
+ return userClaims;
+ }
+
private String generateInitialUserPassword() {
int passwordLength = 6;
//defining the pool of characters to be used for initial password generation
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java
index 742dc62037c..75f560ba68a 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java
@@ -45,12 +45,14 @@ public class Constants {
public static final String USER_CLAIM_LAST_NAME = "http://wso2.org/claims/lastname";
public static final String USER_CLAIM_CREATED = "http://wso2.org/claims/created";
public static final String USER_CLAIM_MODIFIED = "http://wso2.org/claims/modified";
+ public static final String USER_CLAIM_DEVICES = "http://wso2.org/claims/devices";
public static final String PRIMARY_USER_STORE = "PRIMARY";
public static final String DEFAULT_STREAM_VERSION = "1.0.0";
public static final String SCOPE = "scope";
public static final String JDBC_USERSTOREMANAGER = "org.wso2.carbon.user.core.jdbc.JDBCUserStoreManager";
public static final String DEFAULT_SIMPLE_DATE_FORMAT = "EEE, d MMM yyyy HH:mm:ss Z";
public static final int DEFAULT_PAGE_LIMIT = 50;
+ public static final String FORWARD_SLASH = "/";
public final class ErrorMessages {