changed device access verification approach

revert-dabc3590
ayyoob 8 years ago
parent 93ed31c584
commit bfb9c62968

@ -24,24 +24,11 @@ function onRequest(context) {
var constants = require("/app/modules/constants.js");
var websocketEndpoint = devicemgtProps["wssURL"].replace("https", "wss");
var jwtService = carbonServer.osgiService(
'org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService');
var jwtClient = jwtService.getJWTClient();
var encodedClientKeys = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (encodedClientKeys) {
var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var resp = tokenUtil.decode(encodedClientKeys).split(":");
var deviceParam = "{\"scope\":\"stats\",\"deviceIdentifiers\":[{\"id\":\"" + device.deviceIdentifier
+ "\", \"type\":\"" + device.type + "\"}]}";
var encodedScope = tokenUtil.encode(deviceParam);
var tokenPair = jwtClient.getAccessToken(resp[0], resp[1], context.user.username,"default",
{"device": encodedScope});
var token = "";
if (tokenPair) {
token = tokenPair.accessToken;
}
websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.android.sense/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
var tokenPair = session.get(constants["TOKEN_PAIR"]);
if (tokenPair) {
var token = parse(tokenPair)["accessToken"];
websocketEndpoint = websocketEndpoint + "/secured-websocket/org.wso2.iot.android.sense/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
return {"device": device, "websocketEndpoint": websocketEndpoint};
}

@ -229,7 +229,7 @@ public class ArduinoServiceImpl implements ArduinoService {
ArduinoConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true);
}
JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient();
String scopes = "arduino_device cdmf/" + ArduinoConstants.DEVICE_TYPE + "/" + deviceId;
String scopes = " device_" + deviceId;
AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(),
apiApplicationKey.getConsumerSecret(), owner, scopes);
//create token

@ -18,30 +18,17 @@
function onRequest(context) {
var log = new Log("stats.js");
var carbonServer = require("carbon").server;
var device = context.unit.params.device;
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var constants = require("/app/modules/constants.js");
var websocketEndpoint = devicemgtProps["wssURL"].replace("https", "wss");
var jwtService = carbonServer.osgiService(
'org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService');
var jwtClient = jwtService.getJWTClient();
var encodedClientKeys = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (encodedClientKeys) {
var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var resp = tokenUtil.decode(encodedClientKeys).split(":");
var deviceParam = "{\"scope\":\"stats\",\"deviceIdentifiers\":[{\"id\":\"" + device.deviceIdentifier
+ "\", \"type\":\"" + device.type + "\"}]}";
var encodedScope = tokenUtil.encode(deviceParam);
var tokenPair = jwtClient.getAccessToken(resp[0], resp[1], context.user.username,"default",
{"device": encodedScope});
var token = "";
if (tokenPair) {
token = tokenPair.accessToken;
}
websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.devices.temperature/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
var tokenPair = session.get(constants["TOKEN_PAIR"]);
if (tokenPair) {
var token = parse(tokenPair)["accessToken"];
websocketEndpoint = websocketEndpoint + "/secured-websocket/org.wso2.iot.devices.temperature/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
return {"device": device, "websocketEndpoint": websocketEndpoint};
}

@ -213,7 +213,7 @@ public class RaspberryPiServiceImpl implements RaspberryPiService {
RaspberrypiConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true);
}
JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient();
String scopes = "cdmf/" + RaspberrypiConstants.DEVICE_TYPE + "/" + deviceId;
String scopes = " device_" + deviceId;
AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(),
apiApplicationKey.getConsumerSecret(), owner, scopes);
//create token

@ -18,30 +18,17 @@
function onRequest(context) {
var log = new Log("stats.js");
var carbonServer = require("carbon").server;
var device = context.unit.params.device;
var devicemgtProps = require("/app/modules/conf-reader/main.js")["conf"];
var constants = require("/app/modules/constants.js");
var websocketEndpoint = devicemgtProps["wssURL"].replace("https", "wss");
var jwtService = carbonServer.osgiService(
'org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService');
var jwtClient = jwtService.getJWTClient();
var encodedClientKeys = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (encodedClientKeys) {
var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var resp = tokenUtil.decode(encodedClientKeys).split(":");
var deviceParam = "{\"scope\":\"stats\",\"deviceIdentifiers\":[{\"id\":\"" + device.deviceIdentifier
+ "\", \"type\":\"" + device.type + "\"}]}";
var encodedScope = tokenUtil.encode(deviceParam);
var tokenPair = jwtClient.getAccessToken(resp[0], resp[1], context.user.username,"default",
{"device": encodedScope});
var token = "";
if (tokenPair) {
token = tokenPair.accessToken;
}
websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.devices.temperature/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
var tokenPair = session.get(constants["TOKEN_PAIR"]);
if (tokenPair) {
var token = parse(tokenPair)["accessToken"];
websocketEndpoint = websocketEndpoint + "/secured-websocket/org.wso2.iot.devices.temperature/1.0.0?" +
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
return {"device": device, "websocketEndpoint": websocketEndpoint};
}

@ -312,13 +312,10 @@ public class VirtualFireAlarmServiceImpl implements VirtualFireAlarmService {
VirtualFireAlarmConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true);
}
JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient();
String device = "{ \"scope\":\"mqtt-publisher mqtt-subscriber\", \"deviceIdentifiers\":[{\"id\":\""+deviceId+"\", " +
"\"type\":\""+VirtualFireAlarmConstants.DEVICE_TYPE+"\"}]}";
Map<String, String> params = new HashMap<String, String>();
params.put("device", Base64.encodeBase64String(device.getBytes()));
String scopes = " device_" + deviceId;
AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(),
apiApplicationKey.getConsumerSecret(), owner,
null, params);
scopes);
String accessToken = accessTokenInfo.getAccessToken();
String refreshToken = accessTokenInfo.getRefreshToken();
XmppAccount newXmppAccount = new XmppAccount();

@ -24,24 +24,13 @@ function onRequest(context) {
var constants = require("/app/modules/constants.js");
var websocketEndpoint = devicemgtProps["wssURL"].replace("https", "wss");
var jwtService = carbonServer.osgiService(
'org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService');
var jwtClient = jwtService.getJWTClient();
var encodedClientKeys = session.get(constants["ENCODED_TENANT_BASED_CLIENT_APP_CREDENTIALS"]);
if (encodedClientKeys) {
var tokenUtil = require("/app/modules/oauth/token-handler-utils.js")["utils"];
var resp = tokenUtil.decode(encodedClientKeys).split(":");
var deviceParam = "{\"scope\":\"stats\",\"deviceIdentifiers\":[{\"id\":\"" + device.deviceIdentifier
+ "\", \"type\":\"" + device.type + "\"}]}";
var encodedScope = tokenUtil.encode(deviceParam);
var tokenPair = jwtClient.getAccessToken(resp[0], resp[1], context.user.username,"default",
{"device": encodedScope});
var token = "";
if (tokenPair) {
token = tokenPair.accessToken;
}
websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.devices.temperature/1.0.0?" +
"token=" + token + "&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
var tokenPair = session.get(constants["TOKEN_PAIR"]);
if (tokenPair) {
var token = parse(tokenPair)["accessToken"];
websocketEndpoint = websocketEndpoint + "/secured-websocket/org.wso2.iot.devices.temperature/1.0.0?" +
"token=" + token + "&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
}
return {"device": device, "websocketEndpoint": websocketEndpoint};
}

@ -58,6 +58,6 @@
</dependencies>
<build>
<finalName>secured-outputui</finalName>
<finalName>secured-websocket</finalName>
</build>
</project>

@ -79,6 +79,22 @@
<groupId>commons-pool.wso2</groupId>
<artifactId>commons-pool</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
</dependencies>
<build>
@ -147,9 +163,18 @@
org.wso2.carbon.identity.oauth2.stub,
org.wso2.carbon.identity.oauth2.stub.dto,
org.wso2.carbon.user.api,
org.wso2.carbon.utils.multitenancy
org.wso2.carbon.utils.multitenancy,
feign,
feign.auth,
feign.codec,
feign.gson,
javax.cache
</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
<Embed-Dependency>
jsr311-api,
feign-jaxrs
</Embed-Dependency>
</instructions>
</configuration>
</plugin>

@ -17,19 +17,52 @@
*/
package org.wso2.carbon.device.mgt.output.adapter.websocket.authorization;
import feign.Feign;
import feign.FeignException;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.jaxrs.JAXRSContract;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authentication.AuthenticationInfo;
import org.wso2.carbon.device.mgt.output.adapter.websocket.constants.WebsocketConstants;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.OAuthRequestInterceptor;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.AuthorizationRequest;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto
.DeviceAccessAuthorizationAdminService;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.DeviceAuthorizationResult;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.DeviceIdentifier;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.Properties;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.Property;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.WebsocketConfig;
import org.wso2.carbon.device.mgt.output.adapter.websocket.util.WebSocketSessionRequest;
import javax.websocket.Session;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* This authorizer crossvalidates the request with device id and device type.
*/
public class DeviceAuthorizer implements Authorizer {
private static final String STATS_SCOPE_IDENTIFIER = "stats";
private static final String DEVICE_MGT_SCOPE_IDENTIFIER = "device-mgt";
private static DeviceAccessAuthorizationAdminService deviceAccessAuthorizationAdminService;
private static final String CDMF_SERVER_BASE_CONTEXT = "/api/device-mgt/v1.0";
private static final String DEVICE_MGT_SERVER_URL = "deviceMgtServerUrl";
private static final String STAT_PERMISSION = "statsPermission";
private static Log logger = LogFactory.getLog(DeviceAuthorizer.class);
private static List<String> statPermissions;
public DeviceAuthorizer() {
Properties properties =
WebsocketConfig.getInstance().getWebsocketValidationConfigs().getAuthorizer().getProperties();
statPermissions = getPermissions(properties);
deviceAccessAuthorizationAdminService = Feign.builder()
.requestInterceptor(new OAuthRequestInterceptor())
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(DeviceAccessAuthorizationAdminService.class, getDeviceMgtServerUrl(properties)
+ CDMF_SERVER_BASE_CONTEXT);
}
@Override
public boolean isAuthorized(AuthenticationInfo authenticationInfo, Session session, String stream) {
@ -37,19 +70,59 @@ public class DeviceAuthorizer implements Authorizer {
Map<String, String> queryParams = webSocketSessionRequest.getQueryParamValuePairs();
String deviceId = queryParams.get("deviceId");
String deviceType = queryParams.get("deviceType");
Object scopeObject = authenticationInfo.getProperties().get(WebsocketConstants.SCOPE_IDENTIFIER);
if (deviceId != null && !deviceId.isEmpty() && deviceType != null && !deviceType.isEmpty()
&& scopeObject != null) {
String scopes[] = (String[]) scopeObject;
String requiredScope = DEVICE_MGT_SCOPE_IDENTIFIER + ":" + deviceType + ":" + deviceId + ":"
+ STATS_SCOPE_IDENTIFIER;
for (String scope : scopes) {
if (requiredScope.equals(scope)) {
return true;
if (deviceId != null && !deviceId.isEmpty() && deviceType != null && !deviceType.isEmpty()) {
AuthorizationRequest authorizationRequest = new AuthorizationRequest();
authorizationRequest.setTenantDomain(authenticationInfo.getTenantDomain());
if (statPermissions != null && !statPermissions.isEmpty()) {
authorizationRequest.setPermissions(statPermissions);
}
authorizationRequest.setUsername(authenticationInfo.getUsername());
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
deviceIdentifier.setId(deviceId);
deviceIdentifier.setType(deviceType);
List<DeviceIdentifier> deviceIdentifiers = new ArrayList<>();
deviceIdentifiers.add(deviceIdentifier);
authorizationRequest.setDeviceIdentifiers(deviceIdentifiers);
try {
DeviceAuthorizationResult deviceAuthorizationResult =
deviceAccessAuthorizationAdminService.isAuthorized(authorizationRequest);
List<DeviceIdentifier> devices = deviceAuthorizationResult.getAuthorizedDevices();
if (devices != null && devices.size() > 0) {
DeviceIdentifier authorizedDevice = devices.get(0);
if (authorizedDevice.getId().equals(deviceId) && authorizedDevice.getType().equals(deviceType)) {
return true;
}
}
} catch (FeignException e) {
//do nothing
}
}
return false;
}
private String getDeviceMgtServerUrl(Properties properties) {
String deviceMgtServerUrl = null;
for (Property property : properties.getProperty()) {
if (property.getName().equals(DEVICE_MGT_SERVER_URL)) {
deviceMgtServerUrl = property.getValue();
break;
}
}
if (deviceMgtServerUrl == null && deviceMgtServerUrl.isEmpty()) {
logger.error("deviceMgtServerUrl can't be empty ");
}
return deviceMgtServerUrl;
}
private List<String> getPermissions(Properties properties) {
List<String> permission = new ArrayList<>();
for (Property property : properties.getProperty()) {
if (property.getName().equals(STAT_PERMISSION)) {
permission.add(property.getValue());
}
}
return permission;
}
}

@ -0,0 +1,176 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.auth.BasicAuthRequestInterceptor;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.jaxrs.JAXRSContract;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.AccessTokenInfo;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.ApiApplicationKey;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.ApiApplicationRegistrationService;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.ApiRegistrationProfile;
import org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto.TokenIssuerService;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.Properties;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.Property;
import org.wso2.carbon.device.mgt.output.adapter.websocket.config.WebsocketConfig;
/**
* This is a request interceptor to add oauth token header.
*/
public class OAuthRequestInterceptor implements RequestInterceptor {
private AccessTokenInfo tokenInfo;
private long refreshTimeOffset;
private static final String API_APPLICATION_REGISTRATION_CONTEXT = "/api-application-registration";
private static final String DEVICE_MANAGEMENT_SERVICE_TAG[] = {"device_management"};
private static final String APPLICATION_NAME = "mqtt_broker";
private static final String PASSWORD_GRANT_TYPE = "password";
private static final String REFRESH_GRANT_TYPE = "refresh_token";
private ApiApplicationRegistrationService apiApplicationRegistrationService;
private TokenIssuerService tokenIssuerService;
private static Log logger = LogFactory.getLog(OAuthRequestInterceptor.class);
private static final String CONNECTION_USERNAME = "username";
private static final String CONNECTION_PASSWORD = "password";
private static final String TOKEN_ENDPOINT = "tokenEndpoint";
private static final String TOKEN_REFRESH_TIME_OFFSET = "tokenRefreshTimeOffset";
private static final String DEVICE_MGT_SERVER_URL = "deviceMgtServerUrl";
private static String username;
private static String password;
private static String tokenEndpoint;
private static String deviceMgtServerUrl;
/**
* Creates an interceptor that authenticates all requests.
*/
public OAuthRequestInterceptor() {
Properties properties =
WebsocketConfig.getInstance().getWebsocketValidationConfigs().getAuthorizer().getProperties();
deviceMgtServerUrl = getDeviceMgtServerUrl(properties);
refreshTimeOffset = getRefreshTimeOffset(properties);
username = getUsername(properties);
password = getPassword(properties);
tokenEndpoint = getTokenEndpoint(properties);
apiApplicationRegistrationService = Feign.builder().requestInterceptor(
new BasicAuthRequestInterceptor(username, password))
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(ApiApplicationRegistrationService.class,
deviceMgtServerUrl + API_APPLICATION_REGISTRATION_CONTEXT);
}
@Override
public void apply(RequestTemplate template) {
if (tokenInfo == null) {
//had to do on demand initialization due to start up error.
ApiRegistrationProfile apiRegistrationProfile = new ApiRegistrationProfile();
apiRegistrationProfile.setApplicationName(APPLICATION_NAME);
apiRegistrationProfile.setIsAllowedToAllDomains(false);
apiRegistrationProfile.setIsMappingAnExistingOAuthApp(false);
apiRegistrationProfile.setTags(DEVICE_MANAGEMENT_SERVICE_TAG);
ApiApplicationKey apiApplicationKey = apiApplicationRegistrationService.register(apiRegistrationProfile);
String consumerKey = apiApplicationKey.getConsumerKey();
String consumerSecret = apiApplicationKey.getConsumerSecret();
tokenIssuerService = Feign.builder().requestInterceptor(
new BasicAuthRequestInterceptor(consumerKey, consumerSecret))
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(TokenIssuerService.class, tokenEndpoint);
tokenInfo = tokenIssuerService.getToken(PASSWORD_GRANT_TYPE, username, password);
}
if (System.currentTimeMillis() + refreshTimeOffset > tokenInfo.getExpires_in()) {
tokenInfo = tokenIssuerService.getToken(REFRESH_GRANT_TYPE, tokenInfo.getRefresh_token());
}
String headerValue = "Bearer " + tokenInfo.getAccess_token();
template.header("Authorization", headerValue);
}
private String getUsername(Properties properties) {
String username = null;
for (Property property : properties.getProperty()) {
if (property.getName().equals(CONNECTION_USERNAME)) {
username = property.getValue();
break;
}
}
if (username == null || username.isEmpty()) {
logger.error("username can't be empty ");
}
return username;
}
private String getPassword(Properties properties) {
String password = null;
for (Property property : properties.getProperty()) {
if (property.getName().equals(CONNECTION_PASSWORD)) {
password = property.getValue();
break;
}
}
if (password == null || password.isEmpty()) {
logger.error("password can't be empty ");
}
return password;
}
private String getDeviceMgtServerUrl(Properties properties) {
String deviceMgtServerUrl = null;
for (Property property : properties.getProperty()) {
if (property.getName().equals(DEVICE_MGT_SERVER_URL)) {
deviceMgtServerUrl = property.getValue();
break;
}
}
if (deviceMgtServerUrl == null || deviceMgtServerUrl.isEmpty()) {
logger.error("deviceMgtServerUrl can't be empty ");
}
return deviceMgtServerUrl;
}
private String getTokenEndpoint(Properties properties) {
String tokenEndpoint = null;
for (Property property : properties.getProperty()) {
if (property.getName().equals(TOKEN_ENDPOINT)) {
tokenEndpoint = property.getValue();
break;
}
}
if (tokenEndpoint == null || tokenEndpoint.isEmpty()) {
logger.error("tokenEndpoint can't be empty ");
}
return tokenEndpoint;
}
private long getRefreshTimeOffset(Properties properties) {
long refreshTimeOffset = 0;
try {
for (Property property : properties.getProperty()) {
if (property.getName().equals(TOKEN_REFRESH_TIME_OFFSET)) {
refreshTimeOffset = Long.parseLong(property.getValue());
break;
}
}
} catch (NumberFormatException e) {
logger.error("refreshTimeOffset should be a number", e);
}
return refreshTimeOffset;
}
}

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client.dto;
/**
* This hold access token info that returned from the api call
*/
public class AccessTokenInfo {
public String token_type;
public long expires_in;
public String refresh_token;
public String access_token;
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
}

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.output.adapter.websocket.authorization.client.dto;
/**
* This holds api application consumer key and secret.
*/
public class ApiApplicationKey {
private String client_id;
private String client_secret;
public String getConsumerKey() {
return this.client_id;
}
public void setClient_id(String consumerKey) {
this.client_id = consumerKey;
}
public String getConsumerSecret() {
return this.client_secret;
}
public void setClient_secret(String consumerSecret) {
this.client_secret = consumerSecret;
}
}

@ -0,0 +1,25 @@
package org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* This is the application registration service that exposed for apimApplicationRegistration
*/
@Path("/register")
public interface ApiApplicationRegistrationService {
/**
* This method is used to register api application
*
* @param registrationProfile contains the necessary attributes that are needed in order to register an app.
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
ApiApplicationKey register(ApiRegistrationProfile registrationProfile);
}

@ -0,0 +1,78 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class ApiRegistrationProfile {
public String applicationName;
public String tags[];
public boolean isAllowedToAllDomains;
public String consumerKey;
public String consumerSecret;
public boolean isMappingAnExistingOAuthApp;
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public boolean isAllowedToAllDomains() {
return isAllowedToAllDomains;
}
public void setIsAllowedToAllDomains(boolean isAllowedToAllDomains) {
this.isAllowedToAllDomains = isAllowedToAllDomains;
}
public boolean isMappingAnExistingOAuthApp() {
return isMappingAnExistingOAuthApp;
}
public void setIsMappingAnExistingOAuthApp(boolean isMappingAnExistingOAuthApp) {
this.isMappingAnExistingOAuthApp = isMappingAnExistingOAuthApp;
}
public String getConsumerKey() {
return consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerSecret() {
return consumerSecret;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
}

@ -0,0 +1,46 @@
package org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto;
import java.util.List;
/**
* DTO of the authorization request
*/
public class AuthorizationRequest {
String tenantDomain;
String username;
List<DeviceIdentifier> deviceIdentifiers;
List<String> permissions;
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<DeviceIdentifier> getDeviceIdentifiers() {
return deviceIdentifiers;
}
public void setDeviceIdentifiers(List<DeviceIdentifier> deviceIdentifiers) {
this.deviceIdentifiers = deviceIdentifiers;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.output.adapter.websocket.authorization.client.dto;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/admin/authorization")
/**
* This interface provided the definition of the device - user access verification service.
*/
public interface DeviceAccessAuthorizationAdminService {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
DeviceAuthorizationResult isAuthorized(AuthorizationRequest authorizationRequest);
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.output.adapter.websocket.authorization.client.dto;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a DeviceAuthorizationResult including a list of authorized devices and a list of unauthorized devices.
*/
public class DeviceAuthorizationResult {
private List<DeviceIdentifier> authorizedDevices = new ArrayList<>();
private List<DeviceIdentifier> unauthorizedDevices = new ArrayList<>();
public List<DeviceIdentifier> getAuthorizedDevices() {
return authorizedDevices;
}
public void setAuthorizedDevices(List<DeviceIdentifier> authorizedDevices) {
this.authorizedDevices = authorizedDevices;
}
public void setUnauthorizedDevices(
List<DeviceIdentifier> unauthorizedDevices) {
this.unauthorizedDevices = unauthorizedDevices;
}
public void addAuthorizedDevice(DeviceIdentifier deviceIdentifier) {
authorizedDevices.add(deviceIdentifier);
}
public List<DeviceIdentifier> getUnauthorizedDevices() {
return unauthorizedDevices;
}
public void addUnauthorizedDevice(DeviceIdentifier deviceIdentifier) {
unauthorizedDevices.add(deviceIdentifier);
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.output.adapter.websocket.authorization.client.dto;
import java.io.Serializable;
/**
* DTO of the device identifier
*/
public class DeviceIdentifier implements Serializable{
private String id;
private String type;
public DeviceIdentifier() {}
public DeviceIdentifier(String id, String type) {
this.id = id;
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type.toLowerCase();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}

@ -0,0 +1,58 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client.dto;
/**
* This class represents an OAuth application populated with necessary data.
*/
public class OAuthApplicationInfo {
public String client_id;
public String client_name;
public String callback_url;
public String client_secret;
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getClient_name() {
return client_name;
}
public void setClient_name(String client_name) {
this.client_name = client_name;
}
public String getCallback_url() {
return callback_url;
}
public void setCallback_url(String callback_url) {
this.callback_url = callback_url;
}
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client.dto;
/**
* This holds the data related to registration.
*/
public class RegisterInfo {
private boolean isRegistered;
private String msg;
public boolean isRegistered() {
return isRegistered;
}
public void setIsRegistered(boolean isRegistered) {
this.isRegistered = isRegistered;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

@ -0,0 +1,65 @@
package org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.client.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class RegistrationProfile {
public String callbackUrl;
public String clientName;
public String tokenScope;
public String owner;
public String grantType;
public String applicationType;
public String getCallbackUrl() {
return callbackUrl;
}
public void setCallbackUrl(String callBackUrl) {
this.callbackUrl = callBackUrl;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getTokenScope() {
return tokenScope;
}
public void setTokenScope(String tokenScope) {
this.tokenScope = tokenScope;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getApplicationType() {
return applicationType;
}
public void setApplicationType(String applicationType) {
this.applicationType = applicationType;
}
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.output.adapter.websocket.authorization.client.dto;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/**
* This hold the api defintion that is used as a contract with netflix feign.
*/
@Path("/token")
public interface TokenIssuerService {
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getToken(@QueryParam("grant_type") String grant, @QueryParam("username") String username,
@QueryParam("password") String password);
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getToken(@QueryParam("grant_type") String grant, @QueryParam("refresh_token") String refreshToken);
}

@ -18,6 +18,8 @@
package org.wso2.carbon.device.mgt.output.adapter.websocket.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.wso2.carbon.device.mgt.output.adapter.websocket.util.WebsocketUtils;
import org.wso2.carbon.utils.CarbonUtils;
@ -34,6 +36,7 @@ public class WebsocketConfig {
private static WebsocketConfig config = new WebsocketConfig();
private WebsocketValidationConfigs websocketValidationConfigs;
private static final Log log = LogFactory.getLog(WebsocketConfig.class);
private static final String WEBSOCKET_VALIDATION_CONFIG_PATH =
CarbonUtils.getEtcCarbonConfigDirPath() + File.separator + "websocket-validation.xml";
@ -62,6 +65,13 @@ public class WebsocketConfig {
}
public WebsocketValidationConfigs getWebsocketValidationConfigs() {
if (websocketValidationConfigs == null) {
try {
init();
} catch (WebsocketValidationConfigurationFailedException e) {
log.error("failed to initialize the config", e);
}
}
return websocketValidationConfigs;
}

@ -58,6 +58,26 @@
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
</dependency>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>jsr311-api</artifactId>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>javax.cache.wso2</artifactId>
</dependency>
</dependencies>
<build>
@ -95,9 +115,20 @@
org.wso2.carbon.user.core.service,
org.wso2.carbon.user.core.tenant,
org.wso2.carbon.user.api,
*;resolution:=optional
feign,
feign.auth,
feign.codec,
feign.gson,
javax.cache,
javax.xml.namespace,
javax.xml.stream,
org.wso2.carbon.base,
org.wso2.carbon.utils
</Import-Package>
<DynamicImport-Package>*</DynamicImport-Package>
<Embed-Dependency>
jsr311-api,
feign-jaxrs
</Embed-Dependency>
</instructions>
</configuration>
</plugin>

@ -18,18 +18,36 @@
package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization;
import feign.Feign;
import feign.FeignException;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.jaxrs.JAXRSContract;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dna.mqtt.moquette.server.IAuthorizer;
import org.wso2.andes.configuration.enums.MQTTAuthoriztionPermissionLevel;
import org.wso2.andes.mqtt.MQTTAuthorizationSubject;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.OAuthRequestInterceptor;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.AuthorizationRequest;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.DeviceAccessAuthorizationAdminService;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.DeviceAuthorizationResult;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.DeviceIdentifier;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.config.AuthorizationConfigurationManager;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.internal.AuthorizationDataHolder;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.util.AuthorizationCacheKey;
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 javax.cache.Cache;
import javax.cache.CacheConfiguration;
import javax.cache.CacheManager;
import javax.cache.Caching;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* Authorize the connecting users against Carbon Permission Model. Intended usage is
@ -39,13 +57,24 @@ import java.util.List;
*/
public class DeviceAccessBasedMQTTAuthorizer implements IAuthorizer {
private static final String SCOPE_IDENTIFIER = "scope";
private static final String UI_EXECUTE = "ui.execute";
private static Log logger = LogFactory.getLog(DeviceAccessBasedMQTTAuthorizer.class);
AuthorizationConfigurationManager MQTTAuthorizationConfiguration;
private static final String CDMF_SERVER_BASE_CONTEXT = "/api/device-mgt/v1.0";
private static final String CACHE_MANAGER_NAME = "mqttAuthorizationCacheManager";
private static final String CACHE_NAME = "mqttAuthorizationCache";
private static DeviceAccessAuthorizationAdminService deviceAccessAuthorizationAdminService;
private static Cache<AuthorizationCacheKey, Boolean> cache;
public DeviceAccessBasedMQTTAuthorizer() {
this.MQTTAuthorizationConfiguration = AuthorizationConfigurationManager.getInstance();
createCache();
deviceAccessAuthorizationAdminService = Feign.builder()
.requestInterceptor(new OAuthRequestInterceptor())
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(DeviceAccessAuthorizationAdminService.class,
MQTTAuthorizationConfiguration.getDeviceMgtServerUrl() + CDMF_SERVER_BASE_CONTEXT);
}
/**
@ -54,35 +83,73 @@ public class DeviceAccessBasedMQTTAuthorizer implements IAuthorizer {
@Override
public boolean isAuthorizedForTopic(MQTTAuthorizationSubject authorizationSubject, String topic,
MQTTAuthoriztionPermissionLevel permissionLevel) {
if (isUserAuthorized(authorizationSubject, MQTTAuthorizationConfiguration.getAdminPermission(), UI_EXECUTE)) {
return true;
}
String topics[] = topic.split("/");
if (topics.length < 3) {
String tenantDomainFromTopic = topics[0];
if (!tenantDomainFromTopic.equals(authorizationSubject.getTenantDomain())) {
return false;
}
String tenantIdFromTopic = topics[0];
if (!tenantIdFromTopic.equals(authorizationSubject.getTenantDomain())) {
return false;
if (topics.length < 3) {
AuthorizationCacheKey authorizationCacheKey = new AuthorizationCacheKey(tenantDomainFromTopic
,authorizationSubject.getUsername(), "", "");
if (cache.get(authorizationCacheKey)) {
return true;
}
AuthorizationRequest authorizationRequest = new AuthorizationRequest();
authorizationRequest.setTenantDomain(tenantDomainFromTopic);
try {
DeviceAuthorizationResult deviceAuthorizationResult =
deviceAccessAuthorizationAdminService.isAuthorized(authorizationRequest);
if (deviceAuthorizationResult != null) {
cache.put(authorizationCacheKey, true);
return true;
}
return false;
} catch (FeignException e) {
return false;
}
}
String deviceType = topics[1];
String deviceId = topics[2];
Object scopeObject = authorizationSubject.getProperties().get(SCOPE_IDENTIFIER);
AuthorizationCacheKey authorizationCacheKey = new AuthorizationCacheKey(tenantDomainFromTopic
,authorizationSubject.getUsername(), deviceId, deviceType);
if (cache.get(authorizationCacheKey)) {
return true;
}
if (!deviceId.isEmpty() && !deviceType.isEmpty() && scopeObject != null) {
List<String> scopes = (List<String>) scopeObject;
String permissionScope = MQTTAuthorizationConfiguration.getMQTTPublisherScopeIdentifier();
if (permissionLevel == MQTTAuthoriztionPermissionLevel.SUBSCRIBE) {
permissionScope = MQTTAuthorizationConfiguration.getMQTTSubscriberScopeIdentifier();
}
String requiredScope = MQTTAuthorizationConfiguration.getDevicemgtScopeIdentifier() + ":" + deviceType + ":"
+ deviceId + ":" + permissionScope;
for (String scope : scopes) {
if (requiredScope.equals(scope)) {
List<String> requiredPermission;
if (permissionLevel == MQTTAuthoriztionPermissionLevel.SUBSCRIBE) {
requiredPermission = MQTTAuthorizationConfiguration.getSubscriberPermissions();
} else {
requiredPermission = MQTTAuthorizationConfiguration.getPublisherPermissions();
}
AuthorizationRequest authorizationRequest = new AuthorizationRequest();
authorizationRequest.setTenantDomain(tenantDomainFromTopic);
if (requiredPermission != null) {
authorizationRequest.setPermissions(requiredPermission);
}
authorizationRequest.setUsername(authorizationSubject.getUsername());
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
deviceIdentifier.setId(deviceId);
deviceIdentifier.setType(deviceType);
List<DeviceIdentifier> deviceIdentifiers = new ArrayList<>();
deviceIdentifiers.add(deviceIdentifier);
authorizationRequest.setDeviceIdentifiers(deviceIdentifiers);
try {
DeviceAuthorizationResult deviceAuthorizationResult =
deviceAccessAuthorizationAdminService.isAuthorized(authorizationRequest);
List<DeviceIdentifier> devices = deviceAuthorizationResult.getAuthorizedDevices();
if (devices != null && devices.size() > 0) {
DeviceIdentifier authorizedDevice = devices.get(0);
if (authorizedDevice.getId().equals(deviceId) && authorizedDevice.getType().equals(deviceType)) {
cache.put(authorizationCacheKey, true);
return true;
}
}
} catch (FeignException e) {
// do nothing.
}
return false;
}
@ -91,6 +158,11 @@ public class DeviceAccessBasedMQTTAuthorizer implements IAuthorizer {
*/
@Override
public boolean isAuthorizedToConnect(MQTTAuthorizationSubject authorizationSubject) {
if (MQTTAuthorizationConfiguration.getConnectionPermission() == null ||
MQTTAuthorizationConfiguration.getConnectionPermission().isEmpty()) {
//allow authenticated client to connect.
return true;
}
return isUserAuthorized(authorizationSubject, MQTTAuthorizationConfiguration.getConnectionPermission()
, UI_EXECUTE);
}
@ -122,4 +194,28 @@ public class DeviceAccessBasedMQTTAuthorizer implements IAuthorizer {
PrivilegedCarbonContext.endTenantFlow();
}
}
/**
* This method is used to create the Caches.
* @return Cachemanager
*/
private void createCache() {
PrivilegedCarbonContext.startTenantFlow();
PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(
MultitenantConstants.SUPER_TENANT_DOMAIN_NAME, true);
try {
CacheManager cacheManager = Caching.getCacheManagerFactory().getCacheManager(CACHE_MANAGER_NAME);
if (MQTTAuthorizationConfiguration.getCacheDuration() == 0) {
cache = cacheManager.getCache(CACHE_NAME);
} else {
cache = cacheManager.<AuthorizationCacheKey, Boolean>createCacheBuilder(CACHE_NAME).
setExpiry(CacheConfiguration.ExpiryType.MODIFIED, new CacheConfiguration.Duration(
TimeUnit.SECONDS, MQTTAuthorizationConfiguration.getCacheDuration())).
setStoreByValue(false).build();
}
} finally {
PrivilegedCarbonContext.endTenantFlow();
}
}
}

@ -0,0 +1,89 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client;
import feign.Feign;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.auth.BasicAuthRequestInterceptor;
import feign.gson.GsonDecoder;
import feign.gson.GsonEncoder;
import feign.jaxrs.JAXRSContract;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.AccessTokenInfo;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.ApiApplicationKey;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.ApiApplicationRegistrationService;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.ApiRegistrationProfile;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto.TokenIssuerService;
import org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.config.AuthorizationConfigurationManager;
/**
* This is a request interceptor to add oauth token header.
*/
public class OAuthRequestInterceptor implements RequestInterceptor {
private AccessTokenInfo tokenInfo;
private long refreshTimeOffset;
private static final String API_APPLICATION_REGISTRATION_CONTEXT = "/api-application-registration";
private static final String DEVICE_MANAGEMENT_SERVICE_TAG[] = {"device_management"};
private static final String APPLICATION_NAME = "mqtt_broker";
private static final String PASSWORD_GRANT_TYPE = "password";
private static final String REFRESH_GRANT_TYPE = "refresh_token";
private ApiApplicationRegistrationService apiApplicationRegistrationService;
private TokenIssuerService tokenIssuerService;
/**
* Creates an interceptor that authenticates all requests.
*/
public OAuthRequestInterceptor() {
refreshTimeOffset = AuthorizationConfigurationManager.getInstance().getTokenRefreshTimeOffset();
String username = AuthorizationConfigurationManager.getInstance().getUsername();
String password = AuthorizationConfigurationManager.getInstance().getPassword();
apiApplicationRegistrationService = Feign.builder().requestInterceptor(
new BasicAuthRequestInterceptor(username, password))
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(ApiApplicationRegistrationService.class,
AuthorizationConfigurationManager.getInstance().getDeviceMgtServerUrl() +
API_APPLICATION_REGISTRATION_CONTEXT);
}
@Override
public void apply(RequestTemplate template) {
if (tokenInfo == null) {
//had to do on demand initialization due to start up error.
ApiRegistrationProfile apiRegistrationProfile = new ApiRegistrationProfile();
apiRegistrationProfile.setApplicationName(APPLICATION_NAME);
apiRegistrationProfile.setIsAllowedToAllDomains(false);
apiRegistrationProfile.setIsMappingAnExistingOAuthApp(false);
apiRegistrationProfile.setTags(DEVICE_MANAGEMENT_SERVICE_TAG);
ApiApplicationKey apiApplicationKey = apiApplicationRegistrationService.register(apiRegistrationProfile);
String consumerKey = apiApplicationKey.getConsumerKey();
String consumerSecret = apiApplicationKey.getConsumerSecret();
String username = AuthorizationConfigurationManager.getInstance().getUsername();
String password = AuthorizationConfigurationManager.getInstance().getPassword();
tokenIssuerService = Feign.builder().requestInterceptor(
new BasicAuthRequestInterceptor(consumerKey, consumerSecret))
.contract(new JAXRSContract()).encoder(new GsonEncoder()).decoder(new GsonDecoder())
.target(TokenIssuerService.class, AuthorizationConfigurationManager.getInstance().getTokenEndpoint());
tokenInfo = tokenIssuerService.getToken(PASSWORD_GRANT_TYPE, username, password);
}
if (System.currentTimeMillis() + refreshTimeOffset > tokenInfo.getExpires_in()) {
tokenInfo = tokenIssuerService.getToken(REFRESH_GRANT_TYPE, tokenInfo.getRefresh_token());
}
String headerValue = "Bearer " + tokenInfo.getAccess_token();
template.header("Authorization", headerValue);
}
}

@ -0,0 +1,57 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This hold access token info that returned from the api call
*/
public class AccessTokenInfo {
public String token_type;
public long expires_in;
public String refresh_token;
public String access_token;
public String getToken_type() {
return token_type;
}
public void setToken_type(String token_type) {
this.token_type = token_type;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
}

@ -0,0 +1,43 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This holds api application consumer key and secret.
*/
public class ApiApplicationKey {
private String client_id;
private String client_secret;
public String getConsumerKey() {
return this.client_id;
}
public void setClient_id(String consumerKey) {
this.client_id = consumerKey;
}
public String getConsumerSecret() {
return this.client_secret;
}
public void setClient_secret(String consumerSecret) {
this.client_secret = consumerSecret;
}
}

@ -0,0 +1,25 @@
package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
/**
* This is the application registration service that exposed for apimApplicationRegistration
*/
@Path("/register")
public interface ApiApplicationRegistrationService {
/**
* This method is used to register api application
*
* @param registrationProfile contains the necessary attributes that are needed in order to register an app.
*/
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
ApiApplicationKey register(ApiRegistrationProfile registrationProfile);
}

@ -0,0 +1,78 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class ApiRegistrationProfile {
public String applicationName;
public String tags[];
public boolean isAllowedToAllDomains;
public String consumerKey;
public String consumerSecret;
public boolean isMappingAnExistingOAuthApp;
public String getApplicationName() {
return applicationName;
}
public void setApplicationName(String applicationName) {
this.applicationName = applicationName;
}
public String[] getTags() {
return tags;
}
public void setTags(String[] tags) {
this.tags = tags;
}
public boolean isAllowedToAllDomains() {
return isAllowedToAllDomains;
}
public void setIsAllowedToAllDomains(boolean isAllowedToAllDomains) {
this.isAllowedToAllDomains = isAllowedToAllDomains;
}
public boolean isMappingAnExistingOAuthApp() {
return isMappingAnExistingOAuthApp;
}
public void setIsMappingAnExistingOAuthApp(boolean isMappingAnExistingOAuthApp) {
this.isMappingAnExistingOAuthApp = isMappingAnExistingOAuthApp;
}
public String getConsumerKey() {
return consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerSecret() {
return consumerSecret;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
}

@ -0,0 +1,46 @@
package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import java.util.List;
/**
* DTO of the authorization request
*/
public class AuthorizationRequest {
String tenantDomain;
String username;
List<DeviceIdentifier> deviceIdentifiers;
List<String> permissions;
public String getTenantDomain() {
return tenantDomain;
}
public void setTenantDomain(String tenantDomain) {
this.tenantDomain = tenantDomain;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public List<DeviceIdentifier> getDeviceIdentifiers() {
return deviceIdentifiers;
}
public void setDeviceIdentifiers(List<DeviceIdentifier> deviceIdentifiers) {
this.deviceIdentifiers = deviceIdentifiers;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}

@ -0,0 +1,41 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/admin/authorization")
/**
* This interface provided the definition of the device - user access verification service.
*/
public interface DeviceAccessAuthorizationAdminService {
@POST
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
DeviceAuthorizationResult isAuthorized(AuthorizationRequest authorizationRequest);
}

@ -0,0 +1,56 @@
/*
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import java.util.ArrayList;
import java.util.List;
/**
* Represents a DeviceAuthorizationResult including a list of authorized devices and a list of unauthorized devices.
*/
public class DeviceAuthorizationResult {
private List<DeviceIdentifier> authorizedDevices = new ArrayList<>();
private List<DeviceIdentifier> unauthorizedDevices = new ArrayList<>();
public List<DeviceIdentifier> getAuthorizedDevices() {
return authorizedDevices;
}
public void setAuthorizedDevices(List<DeviceIdentifier> authorizedDevices) {
this.authorizedDevices = authorizedDevices;
}
public void setUnauthorizedDevices(
List<DeviceIdentifier> unauthorizedDevices) {
this.unauthorizedDevices = unauthorizedDevices;
}
public void addAuthorizedDevice(DeviceIdentifier deviceIdentifier) {
authorizedDevices.add(deviceIdentifier);
}
public List<DeviceIdentifier> getUnauthorizedDevices() {
return unauthorizedDevices;
}
public void addUnauthorizedDevice(DeviceIdentifier deviceIdentifier) {
unauthorizedDevices.add(deviceIdentifier);
}
}

@ -0,0 +1,51 @@
/*
* Copyright (c) 2014, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import java.io.Serializable;
/**
* DTO of the device identifier
*/
public class DeviceIdentifier implements Serializable{
private String id;
private String type;
public DeviceIdentifier() {}
public DeviceIdentifier(String id, String type) {
this.id = id;
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type.toLowerCase();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}

@ -0,0 +1,58 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This class represents an OAuth application populated with necessary data.
*/
public class OAuthApplicationInfo {
public String client_id;
public String client_name;
public String callback_url;
public String client_secret;
public String getClient_id() {
return client_id;
}
public void setClient_id(String client_id) {
this.client_id = client_id;
}
public String getClient_name() {
return client_name;
}
public void setClient_name(String client_name) {
this.client_name = client_name;
}
public String getCallback_url() {
return callback_url;
}
public void setCallback_url(String callback_url) {
this.callback_url = callback_url;
}
public String getClient_secret() {
return client_secret;
}
public void setClient_secret(String client_secret) {
this.client_secret = client_secret;
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This holds the data related to registration.
*/
public class RegisterInfo {
private boolean isRegistered;
private String msg;
public boolean isRegistered() {
return isRegistered;
}
public void setIsRegistered(boolean isRegistered) {
this.isRegistered = isRegistered;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}

@ -0,0 +1,65 @@
package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.client.dto;
/**
* This class represents the data that are required to register
* the oauth application.
*/
public class RegistrationProfile {
public String callbackUrl;
public String clientName;
public String tokenScope;
public String owner;
public String grantType;
public String applicationType;
public String getCallbackUrl() {
return callbackUrl;
}
public void setCallbackUrl(String callBackUrl) {
this.callbackUrl = callBackUrl;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getTokenScope() {
return tokenScope;
}
public void setTokenScope(String tokenScope) {
this.tokenScope = tokenScope;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getGrantType() {
return grantType;
}
public void setGrantType(String grantType) {
this.grantType = grantType;
}
public String getApplicationType() {
return applicationType;
}
public void setApplicationType(String applicationType) {
this.applicationType = applicationType;
}
}

@ -0,0 +1,37 @@
/*
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed 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.andes.extensions.device.mgt.mqtt.authorization.client.dto;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
/**
* This hold the api defintion that is used as a contract with netflix feign.
*/
@Path("/token")
public interface TokenIssuerService {
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getToken(@QueryParam("grant_type") String grant, @QueryParam("username") String username,
@QueryParam("password") String password);
@POST
@Produces(MediaType.APPLICATION_JSON)
AccessTokenInfo getToken(@QueryParam("grant_type") String grant, @QueryParam("refresh_token") String refreshToken);
}

@ -21,23 +21,34 @@ package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.config;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.util.ArrayList;
import java.util.List;
public class AuthorizationConfigurationManager {
private static final String CONNECTION_PERMISSION = "connectionPermission";
private static final String ADMIN_PERMISSION = "adminPermission";
private static final String MQTT_PUBLISHER_SCOPE_IDENTIFIER = "MQTTPublisherScopeIdentifier";
private static final String MQTT_SUBSCRIBER_SCOPE_IDENTIFIER = "MQTTSubscriberScopeIdentifier";
private static final String DEVICE_MGT_SCOPE_IDENTIFIER = "devicemgtScopeIdentifier";
private static final String MQTT_PUBLISHER_PERMISSION = "publisherPermission";
private static final String MQTT_SUBSCRIBER_PERMISSION = "subscriberPermission";
private static final String CONNECTION_USERNAME = "username";
private static final String CONNECTION_PASSWORD = "password";
private static final String TOKEN_ENDPOINT = "tokenEndpoint";
private static final String TOKEN_REFRESH_TIME_OFFSET = "tokenRefreshTimeOffset";
private static final String DEVICE_MGT_SERVER_URL = "deviceMgtServerUrl";
private static final String MQTT_CACHE_DURATION = "cacheDurationSeconds";
private static final AuthorizationConfigurationManager oAuthConfigurationManager
= new AuthorizationConfigurationManager();
private static Log logger = LogFactory.getLog(AuthorizationConfigurationManager.class);
private String connectionPermission;
private String adminPermission;
private String MQTTPublisherScopeIdentifier;
private String MQTTSubscriberScopeIdentifier;
private String devicemgtScopeIdentifier;
private String username;
private String password;
private String tokenEndpoint;
private long tokenRefreshTimeOffset;
private String deviceMgtServerUrl;
private long cacheDuration;
private List<String> publisherPermissions = new ArrayList<>();
private List<String> subscriberPermissions = new ArrayList<>();
private AuthorizationConfigurationManager() {
@ -59,51 +70,104 @@ public class AuthorizationConfigurationManager {
}
}
public String getAdminPermission() {
return adminPermission;
public List<String> getPublisherPermissions() {
return publisherPermissions;
}
public void setAdminPermission(String adminPermission) {
if (adminPermission != null) {
this.adminPermission = adminPermission;
public void setPublisherPermission(String publisherPermission) {
if (publisherPermission != null && !publisherPermission.isEmpty()) {
this.publisherPermissions.add(publisherPermission);
} else {
logger.error("admin permission can't be null ");
logger.error("MQTT publisher permission can't be empty ");
}
}
public String getMQTTPublisherScopeIdentifier() {
return MQTTPublisherScopeIdentifier;
public List<String> getSubscriberPermissions() {
return subscriberPermissions;
}
public void setMQTTPublisherScopeIdentifier(String MQTTPublisherScopeIdentifier) {
if (MQTTPublisherScopeIdentifier != null) {
this.MQTTPublisherScopeIdentifier = MQTTPublisherScopeIdentifier;
public void setSubscriberPermission(String subscriberPermission) {
if (subscriberPermission != null && !subscriberPermission.isEmpty()) {
this.subscriberPermissions.add(subscriberPermission);
} else {
logger.error("MQTT publisher scope identifier can't be null ");
logger.error("MQTT subscriber permissions can't be null ");
}
}
public String getMQTTSubscriberScopeIdentifier() {
return MQTTSubscriberScopeIdentifier;
public String getUsername() {
return username;
}
public void setUsername(String username) {
if (username != null && !username.isEmpty()) {
this.username = username;
} else {
logger.error("username can't be empty ");
}
}
public String getPassword() {
return password;
}
public void setMQTTSubscriberScopeIdentifier(String MQTTSubscriberScopeIdentifier) {
if (MQTTSubscriberScopeIdentifier != null) {
this.MQTTSubscriberScopeIdentifier = MQTTSubscriberScopeIdentifier;
public void setPassword(String password) {
if (password != null && !password.isEmpty()) {
this.password = password;
} else {
logger.error("MQTT subscriber scope identifier can't be null ");
logger.error("password can't be empty ");
}
}
public String getDevicemgtScopeIdentifier() {
return devicemgtScopeIdentifier;
public String getTokenEndpoint() {
return tokenEndpoint;
}
public void setDevicemgtScopeIdentifier(String devicemgtScopeIdentifier) {
if (devicemgtScopeIdentifier != null) {
this.devicemgtScopeIdentifier = devicemgtScopeIdentifier;
public void setTokenEndpoint(String tokenEndpoint) {
if (tokenEndpoint != null && !tokenEndpoint.isEmpty()) {
this.tokenEndpoint = tokenEndpoint;
} else {
logger.error("Device management scope identifier can't be null ");
logger.error("tokenEndpoint can't be empty ");
}
}
public long getTokenRefreshTimeOffset() {
return tokenRefreshTimeOffset;
}
public void setTokenRefreshTimeOffset(String tokenRefreshTimeOffset) {
try {
if (tokenRefreshTimeOffset != null && !tokenRefreshTimeOffset.isEmpty()) {
this.tokenRefreshTimeOffset = Long.parseLong(tokenRefreshTimeOffset);
}
} catch (NumberFormatException e) {
logger.error("tokenRefreshTimeOffset is not a number(long)");
}
}
public String getDeviceMgtServerUrl() {
return deviceMgtServerUrl;
}
public void setDeviceMgtServerUrl(String deviceMgtServerUrl) {
if (deviceMgtServerUrl != null && !deviceMgtServerUrl.isEmpty()) {
this.deviceMgtServerUrl = deviceMgtServerUrl;
} else {
logger.error("deviceMgtServerUrl can't be empty ");
}
}
public long getCacheDuration() {
return cacheDuration;
}
public void setCacheDuration(String cacheDuration) {
try {
if (cacheDuration != null && !cacheDuration.isEmpty()) {
this.cacheDuration = Long.parseLong(cacheDuration);
}
} catch (NumberFormatException e) {
this.cacheDuration = 0;
}
}
@ -120,18 +184,32 @@ public class AuthorizationConfigurationManager {
case CONNECTION_PERMISSION:
setConnectionPermission(propertyValue);
break;
case ADMIN_PERMISSION:
setAdminPermission(propertyValue);
case MQTT_PUBLISHER_PERMISSION:
setPublisherPermission(propertyValue);
break;
case MQTT_SUBSCRIBER_PERMISSION:
setSubscriberPermission(propertyValue);
break;
case CONNECTION_USERNAME:
setUsername(propertyValue);
break;
case MQTT_PUBLISHER_SCOPE_IDENTIFIER:
setMQTTPublisherScopeIdentifier(propertyValue);
case CONNECTION_PASSWORD:
setPassword(propertyValue);
break;
case MQTT_SUBSCRIBER_SCOPE_IDENTIFIER:
setMQTTSubscriberScopeIdentifier(propertyValue);
case TOKEN_ENDPOINT:
setTokenEndpoint(propertyValue);
break;
case DEVICE_MGT_SCOPE_IDENTIFIER:
setDevicemgtScopeIdentifier(propertyValue);
case TOKEN_REFRESH_TIME_OFFSET:
setTokenRefreshTimeOffset(propertyValue);
break;
case DEVICE_MGT_SERVER_URL:
setDeviceMgtServerUrl(propertyValue);
break;
case MQTT_CACHE_DURATION:
setCacheDuration(propertyValue);
break;
default:
break;
}

@ -0,0 +1,33 @@
package org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization.util;
public class AuthorizationCacheKey {
String tenantDomain;
String deviceId;
String deviceType;
String username;
public AuthorizationCacheKey(String tenantDomain, String username, String deviceId, String deviceType) {
this.username = username;
this.tenantDomain = tenantDomain;
this.deviceId = deviceId;
this.deviceType = deviceType;
}
@Override
public int hashCode() {
int result = this.deviceType.hashCode();
result = 31 * result + ("@" + this.deviceId + "@" + this.tenantDomain + "@" + this.username).hashCode();
return result;
}
@Override
public boolean equals(Object obj) {
return (obj instanceof AuthorizationCacheKey) && deviceType.equals(
((AuthorizationCacheKey) obj).deviceType) && tenantDomain.equals(
((AuthorizationCacheKey) obj).tenantDomain ) && deviceId.equals(
((AuthorizationCacheKey) obj).deviceId) && username.equals(
((AuthorizationCacheKey) obj).username);
}
}

@ -39,6 +39,18 @@
<groupId>org.wso2.carbon.devicemgt-plugins</groupId>
<artifactId>org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
@ -66,6 +78,12 @@
<bundleDef>
org.wso2.carbon.devicemgt-plugins:org.wso2.carbon.andes.extensions.device.mgt.mqtt.authorization:${carbon.devicemgt.plugins.version}
</bundleDef>
<bundleDef>
io.github.openfeign:feign-core:${io.github.openfeign.version}
</bundleDef>
<bundleDef>
io.github.openfeign:feign-gson:${io.github.openfeign.version}
</bundleDef>
</bundles>
<importFeatures>
<importFeatureDef>org.wso2.carbon.core.server:4.4.9</importFeatureDef>

@ -68,6 +68,16 @@
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
<version>${io.github.openfeign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>${io.github.openfeign.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
@ -92,7 +102,7 @@
<outputDirectory>
${project.build.directory}/maven-shared-archive-resources/webapps/
</outputDirectory>
<destFileName>secured-outputui.war</destFileName>
<destFileName>secured-websocket.war</destFileName>
</artifactItem>
</artifactItems>
</configuration>
@ -180,6 +190,12 @@
<bundleDef>
com.jayway.jsonpath:json-path
</bundleDef>
<bundleDef>
io.github.openfeign:feign-core:${io.github.openfeign.version}
</bundleDef>
<bundleDef>
io.github.openfeign:feign-gson:${io.github.openfeign.version}
</bundleDef>
</bundles>
<importFeatures>
<importFeatureDef>

@ -35,5 +35,16 @@
</Authenticator>
<!--Authorizer holds the information of the authorizer that is used authorize a connection.-->
<Authorizer class="org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.DeviceAuthorizer"></Authorizer>
<Authorizer class="org.wso2.carbon.device.mgt.output.adapter.websocket.authorization.DeviceAuthorizer">
<Properties>
<!--websocket connection permissions which are validated for grouping (can have multiple permission.)-->
<Property name="statsPermission">/permission/device-mgt/user/groups/device_monitor</Property>
<Property name="username">admin</Property>
<Property name="password">admin</Property>
<Property name="tokenEndpoint">https://localhost:9443/oauth2</Property>
<!--offset time from expiry time to trigger refresh call (in seconds)-->
<Property name="tokenRefreshTimeOffset">100</Property>
<Property name="deviceMgtServerUrl">https://localhost:9443</Property>
</Properties>
</Authorizer>
</WebsocketValidationConfigs>

@ -239,6 +239,11 @@
<artifactId>org.wso2.carbon.ndatasource.core</artifactId>
<version>${carbon.kernel.version}</version>
</dependency>
<dependency>
<groupId>org.wso2.carbon</groupId>
<artifactId>javax.cache.wso2</artifactId>
<version>${carbon.kernel.version}</version>
</dependency>
<!-- Device Management Core dependencies -->
<dependency>
@ -1155,6 +1160,21 @@
<artifactId>commons-lang</artifactId>
<version>${commons.lang.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>${io.github.openfeign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jaxrs</artifactId>
<version>${io.github.openfeign.version}</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-gson</artifactId>
<version>${io.github.openfeign.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
@ -1317,6 +1337,10 @@
<!-- MB Features -->
<carbon.messaging.version>3.1.11</carbon.messaging.version>
<!--Feign Version-->
<io.github.openfeign.version>9.3.1</io.github.openfeign.version>
<javax.ws.rs.jsr311-api.version>[1.1.0, 2.0.0)</javax.ws.rs.jsr311-api.version>
</properties>
<scm>

Loading…
Cancel
Save