Skip to content

Commit

Permalink
feat: adding scopes in config-api endpoint access token based on tags…
Browse files Browse the repository at this point in the history
… (admin-ui) #6413 (#6414)

Signed-off-by: Arnab Dutta <arnab.bdutta@gmail.com>
  • Loading branch information
duttarnab authored Oct 31, 2023
1 parent 39e55e1 commit 643ba07
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 174 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Objects;

public class AdminPermission {
private String tag;
private String permission;
private String description;
private Boolean defaultPermissionInToken;
Expand Down Expand Up @@ -31,25 +32,34 @@ public void setDefaultPermissionInToken(Boolean defaultPermissionInToken) {
this.defaultPermissionInToken = defaultPermissionInToken;
}

public String getTag() {
return tag;
}

public void setTag(String tag) {
this.tag = tag;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AdminPermission that = (AdminPermission) o;
return permission.equals(that.permission);
return tag.equals(that.tag) && permission.equals(that.permission) && Objects.equals(description, that.description) && Objects.equals(defaultPermissionInToken, that.defaultPermissionInToken);
}

@Override
public int hashCode() {
return Objects.hash(permission);
return Objects.hash(tag, permission, description, defaultPermissionInToken);
}

@Override
public String toString() {
return "AdminPermission{" +
"permission='" + permission + '\'' +
"tag='" + tag + '\'' +
", permission='" + permission + '\'' +
", description='" + description + '\'' +
", defaultPermissionInToken='" + defaultPermissionInToken + '\'' +
", defaultPermissionInToken=" + defaultPermissionInToken +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.jans.ca.plugin.adminui.model.auth;

import java.util.List;

public class ApiTokenRequest {
private List<String> permissionTag;
private String ujwt;

public List<String> getPermissionTag() {
return permissionTag;
}

public void setPermissionTag(List<String> permissionTag) {
this.permissionTag = permissionTag;
}

public String getUjwt() {
return ujwt;
}

public void setUjwt(String ujwt) {
this.ujwt = ujwt;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package io.jans.ca.plugin.adminui.rest.auth;

import io.jans.ca.plugin.adminui.model.auth.ApiTokenRequest;
import io.jans.ca.plugin.adminui.model.auth.OAuth2ConfigResponse;
import io.jans.ca.plugin.adminui.model.auth.TokenResponse;
import io.jans.ca.plugin.adminui.model.auth.UserInfoRequest;
import io.jans.ca.plugin.adminui.model.auth.UserInfoResponse;
import io.jans.ca.plugin.adminui.model.config.AUIConfiguration;
import io.jans.ca.plugin.adminui.model.exception.ApplicationException;
import io.jans.ca.plugin.adminui.service.auth.OAuth2Service;
Expand Down Expand Up @@ -76,37 +75,13 @@ public Response getOAuth2Config(@PathParam("appType") String appType) {
}
}

@GET
@Path(OAUTH2_ACCESS_TOKEN)
@Produces(MediaType.APPLICATION_JSON)
public Response getAccessToken(@QueryParam("code") String code, @PathParam("codeVerifier") String codeVerifier, @PathParam("appType") String appType) {
try {
log.info("Access token request to Auth Server.");
TokenResponse tokenResponse = oAuth2Service.getAccessToken(code, codeVerifier, appType);
log.info("Access token received from Auth Server.");
return Response.ok(tokenResponse).build();
} catch (ApplicationException e) {
log.error(ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription(), e);
return Response
.status(e.getErrorCode())
.entity(CommonUtils.createGenericResponse(false, e.getErrorCode(), e.getMessage()))
.build();
} catch (Exception e) {
log.error(ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription(), e);
return Response
.serverError()
.entity(CommonUtils.createGenericResponse(false, 500, ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription()))
.build();
}
}

@GET
@POST
@Path(OAUTH2_API_PROTECTION_TOKEN)
@Produces(MediaType.APPLICATION_JSON)
public Response getApiProtectionToken(@QueryParam("ujwt") String ujwt, @PathParam("appType") String appType) {
public Response getApiProtectionToken(@Valid @NotNull ApiTokenRequest apiTokenRequest, @PathParam("appType") String appType) {
try {
log.info("Api protection token request to Auth Server.");
TokenResponse tokenResponse = oAuth2Service.getApiProtectionToken(ujwt, appType);
TokenResponse tokenResponse = oAuth2Service.getApiProtectionToken(apiTokenRequest, appType);
log.info("Api protection token received from Auth Server.");
return Response.ok(tokenResponse).build();
} catch (ApplicationException e) {
Expand All @@ -124,28 +99,4 @@ public Response getApiProtectionToken(@QueryParam("ujwt") String ujwt, @PathPara
}
}

@POST
@Path(OAUTH2_API_USER_INFO)
@Produces(MediaType.APPLICATION_JSON)
public Response getUserInfo(@Valid @NotNull UserInfoRequest userInfoRequest, @PathParam("appType") String appType) {
try {
log.info("Get User-Info request to Auth Server.");
UserInfoResponse userInfoResponse = oAuth2Service.getUserInfo(userInfoRequest, appType);
log.info("Get User-Info received from Auth Server.");
return Response.ok(userInfoResponse).build();
} catch (ApplicationException e) {
log.error(ErrorResponse.GET_USER_INFO_ERROR.getDescription(), e);
return Response
.status(e.getErrorCode())
.entity(CommonUtils.createGenericResponse(false, e.getErrorCode(), e.getMessage()))
.build();
} catch (Exception e) {
log.error(ErrorResponse.GET_USER_INFO_ERROR.getDescription(), e);
return Response
.serverError()
.entity(CommonUtils.createGenericResponse(false, 500, ErrorResponse.GET_USER_INFO_ERROR.getDescription()))
.build();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class BaseService {
Logger log;

public io.jans.as.client.TokenResponse getToken(TokenRequest tokenRequest, String tokenEndpoint) {
return getToken(tokenRequest, tokenEndpoint, null);
return getToken(tokenRequest, tokenEndpoint, null, null);
}

/**
Expand All @@ -42,7 +42,7 @@ public io.jans.as.client.TokenResponse getToken(TokenRequest tokenRequest, Strin
* @param userInfoJwt This is the JWT that is returned from the userinfo endpoint.
* @return A TokenResponse object
*/
public io.jans.as.client.TokenResponse getToken(TokenRequest tokenRequest, String tokenEndpoint, String userInfoJwt) {
public io.jans.as.client.TokenResponse getToken(TokenRequest tokenRequest, String tokenEndpoint, String userInfoJwt, List<String> permissionTags) {

try {
MultivaluedMap<String, String> body = new MultivaluedHashMap<>();
Expand All @@ -58,6 +58,10 @@ public io.jans.as.client.TokenResponse getToken(TokenRequest tokenRequest, Strin
body.putSingle("ujwt", userInfoJwt);
}

if (permissionTags != null && !permissionTags.isEmpty()) {
body.put("permission_tag", Collections.singletonList(String.join(" ", permissionTags)));
}

if (!Strings.isNullOrEmpty(tokenRequest.getCodeVerifier())) {
body.putSingle("code_verifier", tokenRequest.getCodeVerifier());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
package io.jans.ca.plugin.adminui.service.auth;

import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import io.jans.as.client.TokenRequest;
import io.jans.as.common.service.common.EncryptionService;
import io.jans.as.model.common.GrantType;
import io.jans.as.model.jwt.Jwt;
import io.jans.as.model.jwt.JwtClaims;
import io.jans.ca.plugin.adminui.model.auth.ApiTokenRequest;
import io.jans.ca.plugin.adminui.model.auth.TokenResponse;
import io.jans.ca.plugin.adminui.model.auth.UserInfoRequest;
import io.jans.ca.plugin.adminui.model.auth.UserInfoResponse;
import io.jans.ca.plugin.adminui.model.config.AUIConfiguration;
import io.jans.ca.plugin.adminui.model.exception.ApplicationException;
import io.jans.ca.plugin.adminui.rest.auth.OAuth2Resource;
import io.jans.ca.plugin.adminui.service.BaseService;
import io.jans.ca.plugin.adminui.service.config.AUIConfigurationService;
import io.jans.ca.plugin.adminui.utils.ClientFactory;
import io.jans.ca.plugin.adminui.utils.CommonUtils;
import io.jans.ca.plugin.adminui.utils.ErrorResponse;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import jakarta.ws.rs.client.Entity;
import jakarta.ws.rs.client.Invocation;
import jakarta.ws.rs.core.MultivaluedHashMap;
import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.slf4j.Logger;

import java.io.UnsupportedEncodingException;
Expand All @@ -50,48 +40,7 @@ public class OAuth2Service extends BaseService {
/**
* Calls token endpoint from the Identity Provider and returns a valid Access Token.
*/
public TokenResponse getAccessToken(String code, String codeVerifier, String appType) throws ApplicationException {
try {
log.debug("Getting access token with code");
if (Strings.isNullOrEmpty(code)) {
log.error(ErrorResponse.AUTHORIZATION_CODE_BLANK.getDescription());
throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.AUTHORIZATION_CODE_BLANK.getDescription());
}
if (Strings.isNullOrEmpty(codeVerifier)) {
log.error(ErrorResponse.CODE_VERIFIER_REQUIRED.getDescription());
throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.CODE_VERIFIER_REQUIRED.getDescription());
}
AUIConfiguration auiConfiguration = auiConfigurationService.getAUIConfiguration(appType);

TokenRequest tokenRequest = new TokenRequest(GrantType.AUTHORIZATION_CODE);
tokenRequest.setCode(code);
tokenRequest.setCodeVerifier(codeVerifier);
tokenRequest.setAuthUsername(auiConfiguration.getAuiWebServerClientId());
tokenRequest.setAuthPassword(encryptionService.decrypt(auiConfiguration.getAuiWebServerClientSecret()));
tokenRequest.setGrantType(GrantType.AUTHORIZATION_CODE);
tokenRequest.setRedirectUri(auiConfiguration.getAuiWebServerRedirectUrl());
tokenRequest.setScope(auiConfiguration.getAuiWebServerScope());
io.jans.as.client.TokenResponse tokenResponse = getToken(tokenRequest, auiConfiguration.getAuiWebServerTokenEndpoint());

TokenResponse tokenResp = new TokenResponse();
tokenResp.setAccessToken(tokenResponse.getAccessToken());
tokenResp.setIdToken(tokenResponse.getIdToken());
tokenResp.setRefreshToken(tokenResponse.getRefreshToken());

return tokenResp;
} catch (ApplicationException e) {
log.error(ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription());
throw e;
} catch (Exception e) {
log.error(ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription(), e);
throw new ApplicationException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ErrorResponse.GET_ACCESS_TOKEN_ERROR.getDescription());
}
}

/**
* Calls token endpoint from the Identity Provider and returns a valid Access Token.
*/
public TokenResponse getApiProtectionToken(String userInfoJwt, String appType) throws ApplicationException {
public TokenResponse getApiProtectionToken(ApiTokenRequest apiTokenRequest, String appType) throws ApplicationException {
try {
log.debug("Getting api-protection token");

Expand All @@ -103,11 +52,17 @@ public TokenResponse getApiProtectionToken(String userInfoJwt, String appType) t
tokenRequest.setGrantType(GrantType.CLIENT_CREDENTIALS);
tokenRequest.setRedirectUri(auiConfiguration.getAuiBackendApiServerRedirectUrl());

if (Strings.isNullOrEmpty(userInfoJwt)) {
log.warn(ErrorResponse.USER_INFO_JWT_BLANK.getDescription());
io.jans.as.client.TokenResponse tokenResponse = null;
if (apiTokenRequest == null) {
tokenRequest.setScope(scopeAsString(Arrays.asList(OAuth2Resource.SCOPE_OPENID)));
tokenResponse = getToken(tokenRequest, auiConfiguration.getAuiBackendApiServerTokenEndpoint());
} else {
if (Strings.isNullOrEmpty(apiTokenRequest.getUjwt())) {
log.warn(ErrorResponse.USER_INFO_JWT_BLANK.getDescription());
tokenRequest.setScope(scopeAsString(Arrays.asList(OAuth2Resource.SCOPE_OPENID)));
}
tokenResponse = getToken(tokenRequest, auiConfiguration.getAuiBackendApiServerTokenEndpoint(), apiTokenRequest.getUjwt(), apiTokenRequest.getPermissionTag());
}
io.jans.as.client.TokenResponse tokenResponse = getToken(tokenRequest, auiConfiguration.getAuiBackendApiServerTokenEndpoint(), userInfoJwt);

final Jwt tokenJwt = Jwt.parse(tokenResponse.getAccessToken());
Map<String, Object> claims = getClaims(tokenJwt);
Expand Down Expand Up @@ -142,65 +97,6 @@ public TokenResponse getApiProtectionToken(String userInfoJwt, String appType) t
}
}

public UserInfoResponse getUserInfo(UserInfoRequest userInfoRequest, String appType) throws ApplicationException {
try {
log.debug("Getting User-Info from auth-server: {}", userInfoRequest.getAccessToken());
AUIConfiguration auiConfiguration = auiConfigurationService.getAUIConfiguration(appType);

String accessToken = org.apache.logging.log4j.util.Strings.isNotBlank(userInfoRequest.getAccessToken()) ? userInfoRequest.getAccessToken() : null;

if (Strings.isNullOrEmpty(userInfoRequest.getCode()) && Strings.isNullOrEmpty(accessToken)) {
log.error(ErrorResponse.CODE_OR_TOKEN_REQUIRED.getDescription());
throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.CODE_OR_TOKEN_REQUIRED.getDescription());
}

if (Strings.isNullOrEmpty(userInfoRequest.getCodeVerifier())) {
log.error(ErrorResponse.CODE_VERIFIER_REQUIRED.getDescription());
throw new ApplicationException(Response.Status.BAD_REQUEST.getStatusCode(), ErrorResponse.CODE_VERIFIER_REQUIRED.getDescription());
}

if (org.apache.logging.log4j.util.Strings.isNotBlank(userInfoRequest.getCode()) && org.apache.logging.log4j.util.Strings.isBlank(accessToken)) {
TokenResponse tokenResponse = getAccessToken(userInfoRequest.getCode(), userInfoRequest.getCodeVerifier(), appType);
accessToken = tokenResponse.getAccessToken();
}
log.debug("Access Token : {}", accessToken);

MultivaluedMap<String, String> body = new MultivaluedHashMap<>();
body.putSingle("access_token", accessToken);

Invocation.Builder request = ClientFactory.instance().getClientBuilder(auiConfiguration.getAuiWebServerUserInfoEndpoint());
request.header("Authorization", "Bearer " + accessToken);

Response response = request
.post(Entity.form(body));

log.debug("User-Info response status code: {}", response.getStatus());

if (response.getStatus() == 200) {
String entity = response.readEntity(String.class);
log.debug("User-Info response entity: {}", entity);
final Jwt jwtUserInfo = Jwt.parse(entity);

log.debug("User-Info response jwtUserInfo: {}", jwtUserInfo);

UserInfoResponse userInfoResponse = new UserInfoResponse();
userInfoResponse.setClaims(getClaims(jwtUserInfo));
userInfoResponse.setJwtUserInfo(entity);

log.debug("User-Info response userInfoResponse: {}", userInfoResponse);
return userInfoResponse;
}

} catch (ApplicationException e) {
log.error(ErrorResponse.GET_USER_INFO_ERROR.getDescription());
throw e;
} catch (Exception e) {
log.error(ErrorResponse.GET_USER_INFO_ERROR.getDescription(), e);
throw new ApplicationException(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ErrorResponse.GET_USER_INFO_ERROR.getDescription());
}
return null;
}

private static String scopeAsString(List<String> scopes) throws UnsupportedEncodingException {
Set<String> scope = Sets.newHashSet();
scope.addAll(scopes);
Expand Down

0 comments on commit 643ba07

Please sign in to comment.