Refactored and fixed findbug issues in OAuth extension

This commit is contained in:
mharindu 2016-04-28 16:43:13 +05:30
parent 04fa0f4874
commit c824e63f1e
2 changed files with 146 additions and 124 deletions

View File

@ -46,7 +46,14 @@ public class OAuthExtUtils {
private static final Log log = LogFactory.getLog(OAuthExtUtils.class); private static final Log log = LogFactory.getLog(OAuthExtUtils.class);
private static final String DEFAULT_SCOPE_NAME = "default"; private static final String DEFAULT_SCOPE_NAME = "default";
private static final String UI_EXECUTE = "ui.execute"; private static final String UI_EXECUTE = "ui.execute";
private static final String REST_API_SCOPE_CACHE = "REST_API_SCOPE_CACHE";
/**
* This method is used to get the tenant id when given tenant domain.
*
* @param tenantDomain Tenant domain name.
* @return Returns the tenant id.
*/
public static int getTenantId(String tenantDomain) { public static int getTenantId(String tenantDomain) {
int tenantId = 0; int tenantId = 0;
if (tenantDomain != null) { if (tenantDomain != null) {
@ -62,9 +69,16 @@ public class OAuthExtUtils {
return tenantId; return tenantId;
} }
/**
* This method is used to set scopes that are authorized to the OAuth token request message context.
*
* @param tokReqMsgCtx OAuth token request message context
* @return Returns true if success.
*/
public static boolean setScopes(OAuthTokenReqMessageContext tokReqMsgCtx) { public static boolean setScopes(OAuthTokenReqMessageContext tokReqMsgCtx) {
String[] requestedScopes = tokReqMsgCtx.getScope(); String[] requestedScopes = tokReqMsgCtx.getScope();
String[] defaultScope = new String[]{DEFAULT_SCOPE_NAME}; String[] defaultScope = new String[]{DEFAULT_SCOPE_NAME};
//If no scopes were requested. //If no scopes were requested.
if (requestedScopes == null || requestedScopes.length == 0) { if (requestedScopes == null || requestedScopes.length == 0) {
tokReqMsgCtx.setScope(defaultScope); tokReqMsgCtx.setScope(defaultScope);
@ -72,7 +86,6 @@ public class OAuthExtUtils {
} }
String consumerKey = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId(); String consumerKey = tokReqMsgCtx.getOauth2AccessTokenReqDTO().getClientId();
String username = tokReqMsgCtx.getAuthorizedUser().getUserName();
List<String> reqScopeList = Arrays.asList(requestedScopes); List<String> reqScopeList = Arrays.asList(requestedScopes);
Map<String, String> restAPIScopesOfCurrentTenant; Map<String, String> restAPIScopesOfCurrentTenant;
@ -80,23 +93,27 @@ public class OAuthExtUtils {
Map<String, String> appScopes; Map<String, String> appScopes;
ApiMgtDAO apiMgtDAO = new ApiMgtDAO(); ApiMgtDAO apiMgtDAO = new ApiMgtDAO();
//Get all the scopes and roles against the scopes defined for the APIs subscribed to the application.
//Get all the scopes and permissions against the scopes defined for the APIs subscribed to the application.
appScopes = apiMgtDAO.getScopeRolesOfApplication(consumerKey); appScopes = apiMgtDAO.getScopeRolesOfApplication(consumerKey);
//Add API Manager rest API scopes set. This list should be loaded at server start up and keep //Add API Manager rest API scopes set. This list should be loaded at server start up and keep
//in memory and add it to each and every request coming. //in memory and add it to each and every request coming.
String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain(); String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain();
restAPIScopesOfCurrentTenant = (Map) Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) restAPIScopesOfCurrentTenant = (Map) Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER)
.getCache("REST_API_SCOPE_CACHE") .getCache(REST_API_SCOPE_CACHE)
.get(tenantDomain); .get(tenantDomain);
if (restAPIScopesOfCurrentTenant != null) { if (restAPIScopesOfCurrentTenant != null) {
appScopes.putAll(restAPIScopesOfCurrentTenant); appScopes.putAll(restAPIScopesOfCurrentTenant);
} else { } else {
restAPIScopesOfCurrentTenant = APIUtil.getRESTAPIScopesFromConfig(APIUtil.getTenantRESTAPIScopesConfig(tenantDomain)); restAPIScopesOfCurrentTenant = APIUtil.
getRESTAPIScopesFromConfig(APIUtil.getTenantRESTAPIScopesConfig(tenantDomain));
//call load tenant config for rest API. //call load tenant config for rest API.
//then put cache //then put cache
appScopes.putAll(restAPIScopesOfCurrentTenant); appScopes.putAll(restAPIScopesOfCurrentTenant);
Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER) Caching.getCacheManager(APIConstants.API_MANAGER_CACHE_MANAGER)
.getCache("REST_API_SCOPE_CACHE") .getCache(REST_API_SCOPE_CACHE)
.put(tenantDomain, restAPIScopesOfCurrentTenant); .put(tenantDomain, restAPIScopesOfCurrentTenant);
} }
//If no scopes can be found in the context of the application //If no scopes can be found in the context of the application
@ -111,37 +128,104 @@ public class OAuthExtUtils {
return true; return true;
} }
int tenantId; // check for authorized scopes
RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService(); List<String> authorizedScopes = getAuthorizedScopes(tokReqMsgCtx, reqScopeList, appScopes);
try { if (!authorizedScopes.isEmpty()) {
tenantId = realmService.getTenantManager().getTenantId(tenantDomain); String[] authScopesArr = authorizedScopes.toArray(new String[authorizedScopes.size()]);
tokReqMsgCtx.setScope(authScopesArr);
} else {
tokReqMsgCtx.setScope(defaultScope);
}
} catch (APIManagementException e) {
log.error("Error while getting scopes of application " + e.getMessage());
return false;
}
return true;
}
// If tenant Id is not set in the tokenReqContext, deriving it from username. /**
if (tenantId == 0 || tenantId == -1) { * Determines if the scope is specified in the white list.
tenantId = IdentityTenantUtil.getTenantIdOfUser(username); *
} * @param scope - The scope key to check
* @return - 'true' if the scope is white listed. 'false' if not.
*/
private static boolean isWhiteListedScope(String scope) {
// load white listed scopes
List<String> scopeSkipList = OAuthExtensionsDataHolder.getInstance().getWhitelistedScopes();
for (String scopeTobeSkipped : scopeSkipList) {
if (scope.matches(scopeTobeSkipped)) {
return true;
}
}
return false;
}
} catch (UserStoreException e) { /**
//Log and return since we do not want to stop issuing the token in case of scope validation failures. * Get the set of default scopes. If a requested scope is matches with the patterns specified in the white list,
log.error("Error when getting the tenant's UserStoreManager or when getting roles of user ", e); * then such scopes will be issued without further validation. If the scope list is empty,
return false; * token will be issued for default scope.
*
* @param requestedScopes - The set of requested scopes
* @return - The subset of scopes that are allowed
*/
private static String[] getAllowedScopes(List<String> requestedScopes) {
List<String> authorizedScopes = new ArrayList<>();
//Iterate the requested scopes list.
for (String scope : requestedScopes) {
if (isWhiteListedScope(scope)) {
authorizedScopes.add(scope);
}
}
if (authorizedScopes.isEmpty()) {
authorizedScopes.add(DEFAULT_SCOPE_NAME);
}
return authorizedScopes.toArray(new String[authorizedScopes.size()]);
}
/**
* This method is used to get the authorized scopes out of requested scopes. It checks requested scopes with app
* scopes whether user has permissions to take actions for the requested scopes.
*
* @param tokReqMsgCtx OAuth token request message context.
* @param reqScopeList Requested scope list.
* @param appScopes App scopes.
* @return Returns a list of scopes.
*/
private static List<String> getAuthorizedScopes(OAuthTokenReqMessageContext tokReqMsgCtx, List<String> reqScopeList,
Map<String, String> appScopes) {
boolean status;
List<String> authorizedScopes = new ArrayList<>();
int tenantId;
String username = tokReqMsgCtx.getAuthorizedUser().getUserName();
String tenantDomain = tokReqMsgCtx.getAuthorizedUser().getTenantDomain();
RealmService realmService = OAuthExtensionsDataHolder.getInstance().getRealmService();
try {
tenantId = realmService.getTenantManager().getTenantId(tenantDomain);
// If tenant Id is not set in the tokenReqContext, deriving it from username.
if (tenantId == 0 || tenantId == -1) {
tenantId = IdentityTenantUtil.getTenantIdOfUser(username);
} }
UserRealm userRealm = OAuthExtensionsDataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId); UserRealm userRealm = OAuthExtensionsDataHolder.getInstance().getRealmService().getTenantUserRealm(tenantId);
List<String> authorizedScopes = new ArrayList<>();
boolean status;
//List<String> userRoleList = new ArrayList<>(Arrays.asList(userRoles));
//Iterate the requested scopes list. //Iterate the requested scopes list.
for (String scope : reqScopeList) { for (String scope : reqScopeList) {
status = false; status = false;
//Get the set of roles associated with the requested scope. //Get the set of roles associated with the requested scope.
String appPermissions = appScopes.get(scope); String appPermissions = appScopes.get(scope);
//If the scope has been defined in the context of the App and if roles have been defined for the scope
//If the scope has been defined in the context of the App and if permissions have been defined for the scope
if (appPermissions != null && appPermissions.length() != 0) { if (appPermissions != null && appPermissions.length() != 0) {
List<String> permissions = new ArrayList<>(Arrays.asList(appPermissions.replaceAll(" ", "").split(","))); List<String> permissions = new ArrayList<>(Arrays.asList(appPermissions.replaceAll(" ", "").split(",")));
//Check if user has at least one of the roles associated with the scope
//Check if user has at least one of the permission associated with the scope
if (!permissions.isEmpty()) { if (!permissions.isEmpty()) {
for (String permission : permissions) { for (String permission : permissions) {
if (userRealm != null && userRealm.getAuthorizationManager() != null) { if (userRealm != null && userRealm.getAuthorizationManager() != null) {
@ -164,66 +248,16 @@ public class OAuthExtUtils {
} }
} }
} }
//The requested scope is defined for the context of the App but no roles have been associated with the scope
//OR
//The scope string starts with 'device_'. //The scope string starts with 'device_'.
else if (appScopes.containsKey(scope) || isWhiteListedScope(scope)) { else if (appScopes.containsKey(scope) || isWhiteListedScope(scope)) {
authorizedScopes.add(scope); authorizedScopes.add(scope);
} }
} }
if (!authorizedScopes.isEmpty()) {
String[] authScopesArr = authorizedScopes.toArray(new String[authorizedScopes.size()]);
tokReqMsgCtx.setScope(authScopesArr);
} else {
tokReqMsgCtx.setScope(defaultScope);
}
} catch (APIManagementException e) {
log.error("Error while getting scopes of application " + e.getMessage());
return false;
} catch (UserStoreException e) { } catch (UserStoreException e) {
e.printStackTrace(); log.error("Error occurred while initializing user store.", e);
} }
return true; return authorizedScopes;
}
/**
* Determines if the scope is specified in the whitelist.
*
* @param scope - The scope key to check
* @return - 'true' if the scope is white listed. 'false' if not.
*/
public static boolean isWhiteListedScope(String scope) {
// load white listed scopes
List<String> scopeSkipList = OAuthExtensionsDataHolder.getInstance().getWhitelistedScopes();
for (String scopeTobeSkipped : scopeSkipList) {
if (scope.matches(scopeTobeSkipped)) {
return true;
}
}
return false;
}
/**
* Get the set of default scopes. If a requested scope is matches with the patterns specified in the whitelist,
* then such scopes will be issued without further validation. If the scope list is empty,
* token will be issued for default scope.
*
* @param requestedScopes - The set of requested scopes
* @return - The subset of scopes that are allowed
*/
private static String[] getAllowedScopes(List<String> requestedScopes) {
List<String> authorizedScopes = new ArrayList<String>();
//Iterate the requested scopes list.
for (String scope : requestedScopes) {
if (isWhiteListedScope(scope)) {
authorizedScopes.add(scope);
}
}
if (authorizedScopes.isEmpty()) {
authorizedScopes.add(DEFAULT_SCOPE_NAME);
}
return authorizedScopes.toArray(new String[authorizedScopes.size()]);
} }
} }

View File

@ -42,12 +42,7 @@ import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.util.UserCoreUtil; import org.wso2.carbon.user.core.util.UserCoreUtil;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -68,7 +63,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
private static final String EMAIL_LOGIN = "EmailLogin"; private static final String EMAIL_LOGIN = "EmailLogin";
private static final String PRIMARY_LOGIN = "primary"; private static final String PRIMARY_LOGIN = "primary";
private Map<String,Map<String,String>> loginConfiguration = new ConcurrentHashMap<>(); private Map<String, Map<String, String>> loginConfiguration = new ConcurrentHashMap<>();
private List<String> requiredHeaderClaimUris = new ArrayList<>(); private List<String> requiredHeaderClaimUris = new ArrayList<>();
@ -103,7 +98,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
boolean isValidated = super.validateGrant(tokReqMsgCtx); boolean isValidated = super.validateGrant(tokReqMsgCtx);
if(isValidated){ if (isValidated) {
int tenantId; int tenantId;
tenantId = IdentityTenantUtil.getTenantIdOfUser(username); tenantId = IdentityTenantUtil.getTenantIdOfUser(username);
@ -120,20 +115,17 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
List<ResponseHeader> respHeaders = new ArrayList<>(); List<ResponseHeader> respHeaders = new ArrayList<>();
if (oAuth2AccessTokenReqDTO.getResourceOwnerUsername() != null) { if (oAuth2AccessTokenReqDTO.getResourceOwnerUsername() != null) {
try { try {
if (requiredHeaderClaimUris != null && !requiredHeaderClaimUris.isEmpty()) { if (requiredHeaderClaimUris != null && !requiredHeaderClaimUris.isEmpty()) {
// Get user's claim values from the default profile. // Get user's claim values from the default profile.
String userStoreDomain = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain(); String userStoreDomain = tokReqMsgCtx.getAuthorizedUser().getUserStoreDomain();
String endUsernameWithDomain = UserCoreUtil.addDomainToName String endUsernameWithDomain = UserCoreUtil.
(oAuth2AccessTokenReqDTO.getResourceOwnerUsername(), addDomainToName(oAuth2AccessTokenReqDTO.getResourceOwnerUsername(), userStoreDomain);
userStoreDomain);
Claim[] mapClaimValues = getUserClaimValues(endUsernameWithDomain,userStoreManager); Claim[] mapClaimValues = getUserClaimValues(endUsernameWithDomain, userStoreManager);
if(mapClaimValues != null && mapClaimValues.length > 0){ if (mapClaimValues != null && mapClaimValues.length > 0) {
ResponseHeader header; ResponseHeader header;
for (String claimUri : requiredHeaderClaimUris) { for (String claimUri : requiredHeaderClaimUris) {
for (Claim claim : mapClaimValues) { for (Claim claim : mapClaimValues) {
@ -146,26 +138,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
} }
} }
} }
} else if (log.isDebugEnabled()) {
log.debug("No claim values for user : " + endUsernameWithDomain);
} }
else if(log.isDebugEnabled()){
log.debug("No claim values for user : "+endUsernameWithDomain);
}
} }
} catch (Exception e) { } catch (Exception e) {
throw new IdentityOAuth2Exception(e.getMessage(), e); throw new IdentityOAuth2Exception("Error occurred while retrieving user claims", e);
} }
} }
tokReqMsgCtx.addProperty("RESPONSE_HEADERS", respHeaders.toArray(new ResponseHeader[respHeaders.size()]));
tokReqMsgCtx.addProperty("RESPONSE_HEADERS", respHeaders.toArray(
new ResponseHeader[respHeaders.size()]));
} }
return isValidated; return isValidated;
} }
@Override @Override
public boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx){ public boolean validateScope(OAuthTokenReqMessageContext tokReqMsgCtx) {
return OAuthExtUtils.setScopes(tokReqMsgCtx); return OAuthExtUtils.setScopes(tokReqMsgCtx);
} }
@ -183,7 +171,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
* *
* @param userId - The username used to login. * @param userId - The username used to login.
* @return <code>true</code> if secondary login name is used, * @return <code>true</code> if secondary login name is used,
* <code>false</code> if primary login name has been used * <code>false</code> if primary login name has been used
*/ */
private boolean isSecondaryLogin(String userId) { private boolean isSecondaryLogin(String userId) {
@ -191,17 +179,14 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
Map<String, String> emailConf = loginConfiguration.get(EMAIL_LOGIN); Map<String, String> emailConf = loginConfiguration.get(EMAIL_LOGIN);
if ("true".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) { if ("true".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) {
return !isUserLoggedInEmail(userId); return !isUserLoggedInEmail(userId);
} } else if ("false".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) {
else if ("false".equalsIgnoreCase(emailConf.get(PRIMARY_LOGIN))) {
return isUserLoggedInEmail(userId); return isUserLoggedInEmail(userId);
} }
} } else if (loginConfiguration.get(USERID_LOGIN) != null) {
else if (loginConfiguration.get(USERID_LOGIN) != null) {
Map<String, String> userIdConf = loginConfiguration.get(USERID_LOGIN); Map<String, String> userIdConf = loginConfiguration.get(USERID_LOGIN);
if ("true".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) { if ("true".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) {
return isUserLoggedInEmail(userId); return isUserLoggedInEmail(userId);
} } else if ("false".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) {
else if ("false".equalsIgnoreCase(userIdConf.get(PRIMARY_LOGIN))) {
return !isUserLoggedInEmail(userId); return !isUserLoggedInEmail(userId);
} }
} }
@ -258,20 +243,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
throws throws
UserStoreException { UserStoreException {
Claim[] userClaims = userClaimsCache.getValueFromCache(authorizedUser); Claim[] userClaims = userClaimsCache.getValueFromCache(authorizedUser);
if(userClaims != null){ if (userClaims != null) {
return userClaims; return userClaims;
}else{ } else {
if(log.isDebugEnabled()){ if (log.isDebugEnabled()) {
log.debug("Cache miss for user claims. Username :" + authorizedUser); log.debug("Cache miss for user claims. Username :" + authorizedUser);
} }
userClaims = userStoreManager.getUserClaimValues( userClaims = userStoreManager.getUserClaimValues(
authorizedUser, null); authorizedUser, null);
userClaimsCache.addToCache(authorizedUser,userClaims); userClaimsCache.addToCache(authorizedUser, userClaims);
return userClaims; return userClaims;
} }
} }
// Read the required claim configuration from identity.xml /**
* Read the required claim configuration from identity.xml
*/
private void parseRequiredHeaderClaimUris(OMElement requiredClaimUrisElem) { private void parseRequiredHeaderClaimUris(OMElement requiredClaimUrisElem) {
if (requiredClaimUrisElem == null) { if (requiredClaimUrisElem == null) {
return; return;
@ -291,21 +278,22 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
/** /**
* Read the primary/secondary login configuration * Read the primary/secondary login configuration
* <OAuth> * <OAuth>
* .... * ....
* <LoginConfig> * <LoginConfig>
* <UserIdLogin primary="true"> * <UserIdLogin primary="true">
* <ClaimUri></ClaimUri> * <ClaimUri></ClaimUri>
* </UserIdLogin> * </UserIdLogin>
* <EmailLogin primary="false"> * <EmailLogin primary="false">
* <ClaimUri>http://wso2.org/claims/emailaddress</ClaimUri> * <ClaimUri>http://wso2.org/claims/emailaddress</ClaimUri>
* </EmailLogin> * </EmailLogin>
* </LoginConfig> * </LoginConfig>
* ..... * .....
* </OAuth> * </OAuth>
*
* @param oauthConfigElem - The '<LoginConfig>' xml configuration element in the api-manager.xml * @param oauthConfigElem - The '<LoginConfig>' xml configuration element in the api-manager.xml
*/ */
private void parseLoginConfig(OMElement oauthConfigElem) { private void parseLoginConfig(OMElement oauthConfigElem) {
OMElement loginConfigElem = oauthConfigElem.getFirstChildWithName(getQNameWithIdentityNS(LOGIN_CONFIG)); OMElement loginConfigElem = oauthConfigElem.getFirstChildWithName(getQNameWithIdentityNS(LOGIN_CONFIG));
if (loginConfigElem != null) { if (loginConfigElem != null) {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Login configuration is set "); log.debug("Login configuration is set ");
@ -313,7 +301,7 @@ public class ExtendedPasswordGrantHandler extends PasswordGrantHandler {
// Primary/Secondary supported login mechanisms // Primary/Secondary supported login mechanisms
OMElement emailConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(EMAIL_LOGIN)); OMElement emailConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(EMAIL_LOGIN));
OMElement userIdConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(USERID_LOGIN)); OMElement userIdConfigElem = loginConfigElem.getFirstChildWithName(getQNameWithIdentityNS(USERID_LOGIN));
Map<String, String> emailConf = new HashMap<String, String>(2); Map<String, String> emailConf = new HashMap<String, String>(2);
emailConf.put(PRIMARY_LOGIN, emailConf.put(PRIMARY_LOGIN,