/*
* Copyright (c) 2013, 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.registry.rest.api.handler;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.cxf.jaxrs.ext.RequestHandler;
import org.apache.cxf.jaxrs.model.ClassResourceInfo;
import org.apache.cxf.message.Message;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.TenantManager;
import org.wso2.carbon.utils.multitenancy.MultitenantUtils;
import javax.ws.rs.core.Response;
public class RestApiRequestAuthorizationHandler implements RequestHandler{
protected Log log = LogFactory.getLog(RestApiRequestAuthorizationHandler.class);
private RealmService service;
/**
* This method handles the request received at the registry endpoint. The method decodes the extracted
* JWT token and extract the enduser's username and tenantID which is appended to the original rest request
* as query param and forwarded to the respective resource class. This method returns
* a null value which indicates that the request to be processed.
*/
@Override
public Response handleRequest(Message message, ClassResourceInfo resourceInfo) {
if(log.isDebugEnabled()){
log.debug("request has been received at REST API handle request method");
}
String requestUrl = message.get(Message.REQUEST_URL).toString();
String queryParam = (message.get(Message.QUERY_STRING) != null)?message.get(Message.QUERY_STRING).toString():"";
//extracting all the headers received along with the request.
String header = message.get(Message.PROTOCOL_HEADERS).toString();
String userName = getUsernameFromJwtToken(header);
String tenantID = null;
try {
tenantID = String.valueOf(getTenantIdOFUser(userName));
}
catch (UserStoreException e) {
log.error(e.getMessage(),e);
return Response.status(Response.Status.UNAUTHORIZED).build();
}
String queryParamAppender = (queryParam.length() == 0 ? "" : "&");
queryParam += queryParamAppender + "username="+userName+"&tenantid="+tenantID;
message.put(Message.REQUEST_URL, requestUrl);
message.put(Message.QUERY_STRING, queryParam);
return null;
}
/**
* this method extract the jwt token header among the request headers, extract the end user's username and returns.
* sample jwt token is given as follows,
*
* eyJ0eXAiOiJKV1QiLCJhbGciOiJTSEEyNTZ3aXRoUlNBIiwieDV0IjoiTm1KbU9HVXh
* NelpsWWpNMlpEUmhOVFpsWVRBMVl6ZGhaVFJpT1dFME5XSTJNMkptT1RjMVpBPT0ifQ==.eyJpc3MiOiJ3c28yLm9yZy9wcm9kd
* WN0cy9hbSIsImV4cCI6MTM2MjcyNDczMzc4NiwiaHR0cDovL3dzbzIub3JnL2NsYWltcy9zdWJzY3JpYmVyIjoiYWRtaW4iLCJo
* dHRwOi8vd3NvMi5vcmcvY2xhaW1zL2FwcGxpY2F0aW9ubmFtZSI6IkRlZmF1bHRBcHBsaWNhdGlvbiIsaHR0cDovL3dzbzIu
* b3JnL2NsYWltcy9hcGljb250ZXh0IjoiL0FBQSIsImh0dHA6Ly93c28yLm9yZy9jbGFpbXMvdmVyc2lvbiI6IjEuMC4wIiwiaHR
* 0cDovL3dzbzIub3JnL2NsYWltcy90aWVyIjoiVW5saW1pdGVkIiwiaHR0cDovL3dzbzIub3JnL2NsYWltcy9lbmR1c2VyIjoi
* YWRtaW4ifQ.Q4Q1ET1SECUT1+OT3AEkNXuUnRg3ssUnWWyOt2Us8boBwjA9AYjnKvDnMqqaOJUjRzWqGdZjoYXycTlTmqFBVdN
* Nq+V4Ol4FMcL5zA3mat4JvYQlvhtqD/3zP0pM7SrLCPQ8uCTWWVlX/y+bUg1F1MoKUGvpmACDbgdLtRT9Btc=
*
* from the above token, extract the part in between two dots(.) and decode that to string variable.
* the decoded string has the informations such as app name, subscriber's username and
* enduser's username ..etc. Then extract the enduser's username and returns to the caller.
*
* After decoded the extracted portion will be as follows:
* {"iss":"wso2.org/products/am","exp":1367660211994,"http://wso2.org/claims/subscriber":"admin",
* "http://wso2.org/claims/applicationid":"10","http://wso2.org/claims/applicationname":"rest",
* "http://wso2.org/claims/applicationtier":"Unlimited","http://wso2.org/claims/apicontext":"/api",
* "http://wso2.org/claims/version":"1.0.0","http://wso2.org/claims/tier":"Gold",
* "http://wso2.org/claims/keytype":"PRODUCTION","http://wso2.org/claims/usertype":"APPLICATION",
* "http://wso2.org/claims/enduser":"admin","http://wso2.org/claims/enduserTenantId":"-1234"}
*
*/
private String getUsernameFromJwtToken(String header){
if(log.isDebugEnabled()){
log.debug("Extracting the username of the enduser from the JWT token");
}
header = header.substring(header.indexOf("x-jwt-assertion"));
header = header.substring(header.indexOf(".")+1, header.lastIndexOf("."));
//decode the jwt token and convert it to string
byte[] jwtBytes = header.getBytes();
byte[] decodedBytes = Base64.decodeBase64(jwtBytes);
String jwtStr = new String(decodedBytes);
//extract the enduser's username and returns
int endUserIndex = jwtStr.indexOf("http://wso2.org/claims/enduser");
jwtStr = jwtStr.substring(endUserIndex+"http://wso2.org/claims/enduser".length()+1);
String endUsername = jwtStr.substring(jwtStr.indexOf('"')+1);
endUsername = endUsername.substring(0, endUsername.indexOf('"'));
return endUsername;
}
private int getTenantIdOFUser(String username) throws UserStoreException {
int tenantId = 0;
String domainName = MultitenantUtils.getTenantDomain(username);
if (domainName != null) {
try {
RealmService service = (RealmService) PrivilegedCarbonContext.getThreadLocalCarbonContext()
.getOSGiService(RealmService.class, null);
TenantManager tenantManager = service.getTenantManager();
tenantId = tenantManager.getTenantId(domainName);
} catch (UserStoreException e) {
String errorMsg = "Error when getting the tenant id from the tenant domain : " +
domainName;
log.error(errorMsg, e);
throw e;
}
}
return tenantId;
}
}