/*
* oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text.
*
* Copyright (c) 2014, Gluu
*/
package org.xdi.oxauth.register.ws.rs;
import static org.xdi.oxauth.model.register.RegisterRequestParam.APPLICATION_TYPE;
import static org.xdi.oxauth.model.register.RegisterRequestParam.CLIENT_NAME;
import static org.xdi.oxauth.model.register.RegisterRequestParam.CLIENT_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.CONTACTS;
import static org.xdi.oxauth.model.register.RegisterRequestParam.DEFAULT_ACR_VALUES;
import static org.xdi.oxauth.model.register.RegisterRequestParam.DEFAULT_MAX_AGE;
import static org.xdi.oxauth.model.register.RegisterRequestParam.FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED;
import static org.xdi.oxauth.model.register.RegisterRequestParam.FRONT_CHANNEL_LOGOUT_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.GRANT_TYPES;
import static org.xdi.oxauth.model.register.RegisterRequestParam.ID_TOKEN_ENCRYPTED_RESPONSE_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.ID_TOKEN_ENCRYPTED_RESPONSE_ENC;
import static org.xdi.oxauth.model.register.RegisterRequestParam.ID_TOKEN_SIGNED_RESPONSE_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.INITIATE_LOGIN_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.JWKS;
import static org.xdi.oxauth.model.register.RegisterRequestParam.JWKS_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.LOGO_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.POLICY_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.POST_LOGOUT_REDIRECT_URIS;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REDIRECT_URIS;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REQUEST_OBJECT_ENCRYPTION_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REQUEST_OBJECT_ENCRYPTION_ENC;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REQUEST_OBJECT_SIGNING_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REQUEST_URIS;
import static org.xdi.oxauth.model.register.RegisterRequestParam.REQUIRE_AUTH_TIME;
import static org.xdi.oxauth.model.register.RegisterRequestParam.RESPONSE_TYPES;
import static org.xdi.oxauth.model.register.RegisterRequestParam.SECTOR_IDENTIFIER_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.SUBJECT_TYPE;
import static org.xdi.oxauth.model.register.RegisterRequestParam.TOKEN_ENDPOINT_AUTH_METHOD;
import static org.xdi.oxauth.model.register.RegisterRequestParam.TOKEN_ENDPOINT_AUTH_SIGNING_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.TOS_URI;
import static org.xdi.oxauth.model.register.RegisterRequestParam.USERINFO_ENCRYPTED_RESPONSE_ALG;
import static org.xdi.oxauth.model.register.RegisterRequestParam.USERINFO_ENCRYPTED_RESPONSE_ENC;
import static org.xdi.oxauth.model.register.RegisterRequestParam.USERINFO_SIGNED_RESPONSE_ALG;
import static org.xdi.oxauth.model.register.RegisterResponseParam.CLIENT_ID_ISSUED_AT;
import static org.xdi.oxauth.model.register.RegisterResponseParam.CLIENT_SECRET;
import static org.xdi.oxauth.model.register.RegisterResponseParam.CLIENT_SECRET_EXPIRES_AT;
import static org.xdi.oxauth.model.register.RegisterResponseParam.REGISTRATION_CLIENT_URI;
import static org.xdi.oxauth.model.util.StringUtils.toList;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.TimeZone;
import java.util.UUID;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.CacheControl;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.xdi.ldap.model.CustomAttribute;
import org.xdi.model.metric.MetricType;
import org.xdi.oxauth.audit.ApplicationAuditLogger;
import org.xdi.oxauth.client.RegisterRequest;
import org.xdi.oxauth.model.audit.Action;
import org.xdi.oxauth.model.audit.OAuth2AuditLog;
import org.xdi.oxauth.model.common.AuthenticationMethod;
import org.xdi.oxauth.model.common.ResponseType;
import org.xdi.oxauth.model.common.Scope;
import org.xdi.oxauth.model.common.SubjectType;
import org.xdi.oxauth.model.config.StaticConfiguration;
import org.xdi.oxauth.model.configuration.AppConfiguration;
import org.xdi.oxauth.model.crypto.signature.SignatureAlgorithm;
import org.xdi.oxauth.model.error.ErrorResponseFactory;
import org.xdi.oxauth.model.register.RegisterErrorResponseType;
import org.xdi.oxauth.model.register.RegisterResponseParam;
import org.xdi.oxauth.model.registration.Client;
import org.xdi.oxauth.model.registration.RegisterParamsValidator;
import org.xdi.oxauth.model.token.HandleTokenFactory;
import org.xdi.oxauth.model.util.Util;
import org.xdi.oxauth.service.ClientService;
import org.xdi.oxauth.service.InumService;
import org.xdi.oxauth.service.MetricService;
import org.xdi.oxauth.service.ScopeService;
import org.xdi.oxauth.service.external.ExternalDynamicClientRegistrationService;
import org.xdi.oxauth.service.token.TokenService;
import org.xdi.oxauth.util.ServerUtil;
import org.xdi.util.StringHelper;
import org.xdi.util.security.StringEncrypter;
/**
* Implementation for register REST web services.
*
* @author Javier Rojas Blum
* @author Yuriy Zabrovarnyy
* @author Yuriy Movchan
* @version October 31, 2016
*/
@Path("/oxauth")
public class RegisterRestWebServiceImpl implements RegisterRestWebService {
@Inject
private Logger log;
@Inject
private ApplicationAuditLogger applicationAuditLogger;
@Inject
private ErrorResponseFactory errorResponseFactory;
@Inject
private ScopeService scopeService;
@Inject
private InumService inumService;
@Inject
private ClientService clientService;
@Inject
private TokenService tokenService;
@Inject
private MetricService metricService;
@Inject
private ExternalDynamicClientRegistrationService externalDynamicClientRegistrationService;
@Inject
private RegisterParamsValidator registerParamsValidator;
@Inject
private AppConfiguration appConfiguration;
@Inject
private StaticConfiguration staticConfiguration;
@Override
public Response requestRegister(String requestParams, String authorization, HttpServletRequest httpRequest, SecurityContext securityContext) {
com.codahale.metrics.Timer.Context timerContext = metricService.getTimer(MetricType.DYNAMIC_CLIENT_REGISTRATION_RATE).time();
try {
return registerClientImpl(requestParams, httpRequest, securityContext);
} finally {
timerContext.stop();
}
}
private Response registerClientImpl(String requestParams, HttpServletRequest httpRequest, SecurityContext securityContext) {
Response.ResponseBuilder builder = Response.ok();
OAuth2AuditLog oAuth2AuditLog = new OAuth2AuditLog(ServerUtil.getIpAddress(httpRequest), Action.CLIENT_REGISTRATION);
try {
if (appConfiguration.getDynamicRegistrationEnabled()) {
final RegisterRequest r = RegisterRequest.fromJson(requestParams);
log.debug("Attempting to register client: applicationType = {}, clientName = {}, redirectUris = {}, isSecure = {}, sectorIdentifierUri = {}, params = {}",
r.getApplicationType(), r.getClientName(), r.getRedirectUris(), securityContext.isSecure(), r.getSectorIdentifierUri(), requestParams);
if (r.getSubjectType() == null) {
SubjectType defaultSubjectType = SubjectType.fromString(appConfiguration.getDefaultSubjectType());
if (defaultSubjectType != null) {
r.setSubjectType(defaultSubjectType);
} else if (appConfiguration.getSubjectTypesSupported().contains(SubjectType.PUBLIC.toString())) {
r.setSubjectType(SubjectType.PUBLIC);
} else if (appConfiguration.getSubjectTypesSupported().contains(SubjectType.PAIRWISE.toString())) {
r.setSubjectType(SubjectType.PAIRWISE);
}
}
if (r.getIdTokenSignedResponseAlg() == null) {
r.setIdTokenSignedResponseAlg(SignatureAlgorithm.fromString(appConfiguration.getDefaultSignatureAlgorithm()));
}
if (r.getIdTokenSignedResponseAlg() != SignatureAlgorithm.NONE) {
if (registerParamsValidator.validateParamsClientRegister(r.getApplicationType(), r.getSubjectType(),
r.getRedirectUris(), r.getSectorIdentifierUri())) {
if (!registerParamsValidator.validateRedirectUris(r.getApplicationType(), r.getSubjectType(),
r.getRedirectUris(), r.getSectorIdentifierUri())) {
builder = Response.status(Response.Status.BAD_REQUEST.getStatusCode());
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_REDIRECT_URI));
} else {
registerParamsValidator.validateLogoutUri(r.getFrontChannelLogoutUris(), r.getRedirectUris(), errorResponseFactory);
String clientsBaseDN = staticConfiguration.getBaseDn().getClients();
String inum = inumService.generateClientInum();
String generatedClientSecret = UUID.randomUUID().toString();
final Client client = new Client();
client.setDn("inum=" + inum + "," + clientsBaseDN);
client.setClientId(inum);
client.setClientSecret(clientService.encryptSecret(generatedClientSecret));
client.setRegistrationAccessToken(HandleTokenFactory.generateHandleToken());
final Calendar calendar = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
client.setClientIdIssuedAt(calendar.getTime());
if (appConfiguration.getDynamicRegistrationExpirationTime() > 0) {
calendar.add(Calendar.SECOND, appConfiguration.getDynamicRegistrationExpirationTime());
client.setClientSecretExpiresAt(calendar.getTime());
}
if (StringUtils.isBlank(r.getClientName()) && r.getRedirectUris() != null && !r.getRedirectUris().isEmpty()) {
try {
URI redUri = new URI(r.getRedirectUris().get(0));
client.setClientName(redUri.getHost());
} catch (Exception e) {
//ignore
log.error(e.getMessage(), e);
client.setClientName("Unknown");
}
}
updateClientFromRequestObject(client, r);
boolean registerClient = true;
if (externalDynamicClientRegistrationService.isEnabled()) {
registerClient = externalDynamicClientRegistrationService.executeExternalUpdateClientMethods(r, client);
}
if (registerClient) {
Date currentTime = Calendar.getInstance().getTime();
client.setLastAccessTime(currentTime);
client.setLastLogonTime(currentTime);
Boolean persistClientAuthorizations = appConfiguration.getDynamicRegistrationPersistClientAuthorizations();
client.setPersistClientAuthorizations(persistClientAuthorizations != null ? persistClientAuthorizations : false);
clientService.persist(client);
JSONObject jsonObject = getJSONObject(client);
builder.entity(jsonObject.toString(4).replace("\\/", "/"));
oAuth2AuditLog.setClientId(client.getClientId());
oAuth2AuditLog.setScope(clientScopesToString(client));
oAuth2AuditLog.setSuccess(true);
} else {
log.trace("Client parameters are invalid, returns invalid_request error.");
builder = Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
}
}
} else {
log.trace("Client parameters are invalid, returns invalid_request error.");
builder = Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
}
} else {
log.debug("The signature algorithm for id_token cannot be none.");
builder = Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
}
} else {
log.debug("Dynamic client registration is disabled.");
builder = Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.ACCESS_DENIED));
}
} catch (StringEncrypter.EncryptionException e) {
builder = internalErrorResponse();
log.error(e.getMessage(), e);
} catch (JSONException e) {
builder = internalErrorResponse();
log.error(e.getMessage(), e);
} catch (WebApplicationException e) {
log.error(e.getMessage(), e);
throw e;
} catch (Exception e) {
builder = internalErrorResponse();
log.error(e.getMessage(), e);
}
builder.cacheControl(ServerUtil.cacheControl(true, false));
builder.header("Pragma", "no-cache");
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return builder.build();
}
public Response.ResponseBuilder internalErrorResponse() {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
}
// yuriyz - ATTENTION : this method is used for both registration and update client metadata cases, therefore any logic here
// will be applied for both cases.
private void updateClientFromRequestObject(Client p_client, RegisterRequest requestObject) throws JSONException {
List<String> redirectUris = requestObject.getRedirectUris();
if (redirectUris != null && !redirectUris.isEmpty()) {
redirectUris = new ArrayList<String>(new HashSet<String>(redirectUris)); // Remove repeated elements
p_client.setRedirectUris(redirectUris.toArray(new String[redirectUris.size()]));
}
if (requestObject.getApplicationType() != null) {
p_client.setApplicationType(requestObject.getApplicationType().toString());
}
if (StringUtils.isNotBlank(requestObject.getClientName())) {
p_client.setClientName(requestObject.getClientName());
}
if (StringUtils.isNotBlank(requestObject.getSectorIdentifierUri())) {
p_client.setSectorIdentifierUri(requestObject.getSectorIdentifierUri());
}
List<ResponseType> responseTypes = requestObject.getResponseTypes();
if (responseTypes != null && !responseTypes.isEmpty()) {
responseTypes = new ArrayList<ResponseType>(new HashSet<ResponseType>(responseTypes)); // Remove repeated elements
p_client.setResponseTypes(responseTypes.toArray(new ResponseType[responseTypes.size()]));
}
List<String> contacts = requestObject.getContacts();
if (contacts != null && !contacts.isEmpty()) {
contacts = new ArrayList<String>(new HashSet<String>(contacts)); // Remove repeated elements
p_client.setContacts(contacts.toArray(new String[contacts.size()]));
}
if (StringUtils.isNotBlank(requestObject.getLogoUri())) {
p_client.setLogoUri(requestObject.getLogoUri());
}
if (StringUtils.isNotBlank(requestObject.getClientUri())) {
p_client.setClientUri(requestObject.getClientUri());
}
if (StringUtils.isNotBlank(requestObject.getPolicyUri())) {
p_client.setPolicyUri(requestObject.getPolicyUri());
}
if (StringUtils.isNotBlank(requestObject.getTosUri())) {
p_client.setTosUri(requestObject.getTosUri());
}
if (StringUtils.isNotBlank(requestObject.getJwksUri())) {
p_client.setJwksUri(requestObject.getJwksUri());
}
if (StringUtils.isNotBlank(requestObject.getJwks())) {
p_client.setJwks(requestObject.getJwks());
}
if (requestObject.getSubjectType() != null) {
p_client.setSubjectType(requestObject.getSubjectType().toString());
}
if (requestObject.getIdTokenSignedResponseAlg() != null
&& requestObject.getIdTokenSignedResponseAlg() != SignatureAlgorithm.NONE) {
p_client.setIdTokenSignedResponseAlg(requestObject.getIdTokenSignedResponseAlg().toString());
}
if (requestObject.getIdTokenEncryptedResponseAlg() != null) {
p_client.setIdTokenEncryptedResponseAlg(requestObject.getIdTokenEncryptedResponseAlg().toString());
}
if (requestObject.getIdTokenEncryptedResponseEnc() != null) {
p_client.setIdTokenEncryptedResponseEnc(requestObject.getIdTokenEncryptedResponseEnc().toString());
}
if (requestObject.getUserInfoSignedResponseAlg() != null) {
p_client.setUserInfoSignedResponseAlg(requestObject.getUserInfoSignedResponseAlg().toString());
}
if (requestObject.getUserInfoEncryptedResponseAlg() != null) {
p_client.setUserInfoEncryptedResponseAlg(requestObject.getUserInfoEncryptedResponseAlg().toString());
}
if (requestObject.getUserInfoEncryptedResponseEnc() != null) {
p_client.setUserInfoEncryptedResponseEnc(requestObject.getUserInfoEncryptedResponseEnc().toString());
}
if (requestObject.getRequestObjectSigningAlg() != null) {
p_client.setRequestObjectSigningAlg(requestObject.getRequestObjectSigningAlg().toString());
}
if (requestObject.getRequestObjectEncryptionAlg() != null) {
p_client.setRequestObjectEncryptionAlg(requestObject.getRequestObjectEncryptionAlg().toString());
}
if (requestObject.getRequestObjectEncryptionEnc() != null) {
p_client.setRequestObjectEncryptionEnc(requestObject.getRequestObjectEncryptionEnc().toString());
}
if (requestObject.getTokenEndpointAuthMethod() != null) {
p_client.setTokenEndpointAuthMethod(requestObject.getTokenEndpointAuthMethod().toString());
} else { // If omitted, the default is client_secret_basic
p_client.setTokenEndpointAuthMethod(AuthenticationMethod.CLIENT_SECRET_BASIC.toString());
}
if (requestObject.getTokenEndpointAuthSigningAlg() != null) {
p_client.setTokenEndpointAuthSigningAlg(requestObject.getTokenEndpointAuthSigningAlg().toString());
}
if (requestObject.getDefaultMaxAge() != null) {
p_client.setDefaultMaxAge(requestObject.getDefaultMaxAge());
}
if (requestObject.getRequireAuthTime() != null) {
p_client.setRequireAuthTime(requestObject.getRequireAuthTime());
}
List<String> defaultAcrValues = requestObject.getDefaultAcrValues();
if (defaultAcrValues != null && !defaultAcrValues.isEmpty()) {
defaultAcrValues = new ArrayList<String>(new HashSet<String>(defaultAcrValues)); // Remove repeated elements
p_client.setDefaultAcrValues(defaultAcrValues.toArray(new String[defaultAcrValues.size()]));
}
if (StringUtils.isNotBlank(requestObject.getInitiateLoginUri())) {
p_client.setInitiateLoginUri(requestObject.getInitiateLoginUri());
}
List<String> postLogoutRedirectUris = requestObject.getPostLogoutRedirectUris();
if (postLogoutRedirectUris != null && !postLogoutRedirectUris.isEmpty()) {
postLogoutRedirectUris = new ArrayList<String>(new HashSet<String>(postLogoutRedirectUris)); // Remove repeated elements
p_client.setPostLogoutRedirectUris(postLogoutRedirectUris.toArray(new String[postLogoutRedirectUris.size()]));
}
if (requestObject.getFrontChannelLogoutUris() != null && !requestObject.getFrontChannelLogoutUris().isEmpty()) {
p_client.setFrontChannelLogoutUri(requestObject.getFrontChannelLogoutUris().toArray(new String[requestObject.getFrontChannelLogoutUris().size()]));
}
p_client.setFrontChannelLogoutSessionRequired(requestObject.getFrontChannelLogoutSessionRequired());
List<String> requestUris = requestObject.getRequestUris();
if (requestUris != null && !requestUris.isEmpty()) {
requestUris = new ArrayList<String>(new HashSet<String>(requestUris)); // Remove repeated elements
p_client.setRequestUris(requestUris.toArray(new String[requestUris.size()]));
}
List<String> scopes = requestObject.getScopes();
List<String> scopesDn;
if (scopes != null && !scopes.isEmpty()
&& appConfiguration.getDynamicRegistrationScopesParamEnabled() != null
&& appConfiguration.getDynamicRegistrationScopesParamEnabled()) {
List<String> defaultScopes = scopeService.getDefaultScopesDn();
List<String> requestedScopes = scopeService.getScopesDn(scopes);
if (defaultScopes.containsAll(requestedScopes)) {
scopesDn = requestedScopes;
p_client.setScopes(scopesDn.toArray(new String[scopesDn.size()]));
} else {
scopesDn = defaultScopes;
p_client.setScopes(scopesDn.toArray(new String[scopesDn.size()]));
}
} else {
scopesDn = scopeService.getDefaultScopesDn();
p_client.setScopes(scopesDn.toArray(new String[scopesDn.size()]));
}
Date clientSecretExpiresAt = requestObject.getClientSecretExpiresAt();
if (clientSecretExpiresAt != null) {
p_client.setClientSecretExpiresAt(clientSecretExpiresAt);
}
if (requestObject.getJsonObject() != null) {
// Custom params
putCustomStuffIntoObject(p_client, requestObject.getJsonObject());
}
}
@Override
public Response requestClientUpdate(String requestParams, String clientId, @HeaderParam("Authorization") String authorization, @Context HttpServletRequest httpRequest, @Context SecurityContext securityContext) {
OAuth2AuditLog oAuth2AuditLog = new OAuth2AuditLog(ServerUtil.getIpAddress(httpRequest), Action.CLIENT_UPDATE);
oAuth2AuditLog.setClientId(clientId);
try {
log.debug("Attempting to UPDATE client, client_id: {}, requestParams = {}, isSecure = {}",
clientId, requestParams, securityContext.isSecure());
final String accessToken = tokenService.getTokenFromAuthorizationParameter(authorization);
if (StringUtils.isNotBlank(accessToken) && StringUtils.isNotBlank(clientId) && StringUtils.isNotBlank(requestParams)) {
final RegisterRequest request = RegisterRequest.fromJson(requestParams);
if (request != null) {
boolean redirectUrisValidated = true;
if (request.getRedirectUris() != null && !request.getRedirectUris().isEmpty()) {
redirectUrisValidated = registerParamsValidator.validateRedirectUris(request.getApplicationType(), request.getSubjectType(),
request.getRedirectUris(), request.getSectorIdentifierUri());
}
if (redirectUrisValidated) {
if (request.getSubjectType() != null
&& !appConfiguration.getSubjectTypesSupported().contains(request.getSubjectType())) {
log.debug("Client UPDATE : parameter subject_type is invalid. Returns BAD_REQUEST response.");
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA)).build();
}
final Client client = clientService.getClient(clientId, accessToken);
if (client != null) {
updateClientFromRequestObject(client, request);
clientService.merge(client);
oAuth2AuditLog.setScope(clientScopesToString(client));
oAuth2AuditLog.setSuccess(true);
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return Response.status(Response.Status.OK).entity(clientAsEntity(client)).build();
} else {
log.trace("The Access Token is not valid for the Client ID, returns invalid_token error.");
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_TOKEN)).build();
}
}
}
}
log.debug("Client UPDATE : parameters are invalid. Returns BAD_REQUEST response.");
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return Response.status(Response.Status.BAD_REQUEST).
entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA)).build();
} catch (Exception e) {
log.error(e.getMessage(), e);
}
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return internalErrorResponse().build();
}
@Override
public Response requestClientRead(String clientId, String authorization, HttpServletRequest httpRequest,
SecurityContext securityContext) {
String accessToken = tokenService.getTokenFromAuthorizationParameter(authorization);
log.debug("Attempting to read client: clientId = {}, registrationAccessToken = {} isSecure = {}",
clientId, accessToken, securityContext.isSecure());
Response.ResponseBuilder builder = Response.ok();
OAuth2AuditLog oAuth2AuditLog = new OAuth2AuditLog(ServerUtil.getIpAddress(httpRequest), Action.CLIENT_READ);
oAuth2AuditLog.setClientId(clientId);
try {
if (appConfiguration.getDynamicRegistrationEnabled()) {
if (registerParamsValidator.validateParamsClientRead(clientId, accessToken)) {
Client client = clientService.getClient(clientId, accessToken);
if (client != null) {
oAuth2AuditLog.setScope(clientScopesToString(client));
oAuth2AuditLog.setSuccess(true);
builder.entity(clientAsEntity(client));
} else {
log.trace("The Access Token is not valid for the Client ID, returns invalid_token error.");
builder = Response.status(Response.Status.BAD_REQUEST.getStatusCode());
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_TOKEN));
}
} else {
log.trace("Client parameters are invalid.");
builder = Response.status(Response.Status.BAD_REQUEST);
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
}
} else {
builder = Response.status(Response.Status.BAD_REQUEST);
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.ACCESS_DENIED));
}
} catch (JSONException e) {
builder = Response.status(500);
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
log.error(e.getMessage(), e);
} catch (StringEncrypter.EncryptionException e) {
builder = Response.status(500);
builder.entity(errorResponseFactory.getErrorAsJson(RegisterErrorResponseType.INVALID_CLIENT_METADATA));
log.error(e.getMessage(), e);
}
CacheControl cacheControl = new CacheControl();
cacheControl.setNoTransform(false);
cacheControl.setNoStore(true);
builder.cacheControl(cacheControl);
builder.header("Pragma", "no-cache");
applicationAuditLogger.sendMessage(oAuth2AuditLog);
return builder.build();
}
private String clientAsEntity(Client p_client) throws JSONException, StringEncrypter.EncryptionException {
final JSONObject jsonObject = getJSONObject(p_client);
return jsonObject.toString(4).replace("\\/", "/");
}
private JSONObject getJSONObject(Client client) throws JSONException, StringEncrypter.EncryptionException {
JSONObject responseJsonObject = new JSONObject();
Util.addToJSONObjectIfNotNull(responseJsonObject, RegisterResponseParam.CLIENT_ID.toString(), client.getClientId());
Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_SECRET.toString(), clientService.decryptSecret(client.getClientSecret()));
Util.addToJSONObjectIfNotNull(responseJsonObject, RegisterResponseParam.REGISTRATION_ACCESS_TOKEN.toString(), client.getRegistrationAccessToken());
Util.addToJSONObjectIfNotNull(responseJsonObject, REGISTRATION_CLIENT_URI.toString(),
appConfiguration.getRegistrationEndpoint() + "?" +
RegisterResponseParam.CLIENT_ID.toString() + "=" + client.getClientId());
responseJsonObject.put(CLIENT_ID_ISSUED_AT.toString(), client.getClientIdIssuedAt().getTime() / 1000);
responseJsonObject.put(CLIENT_SECRET_EXPIRES_AT.toString(), client.getClientSecretExpiresAt() != null && client.getClientSecretExpiresAt().getTime() > 0 ?
client.getClientSecretExpiresAt().getTime() / 1000 : 0);
Util.addToJSONObjectIfNotNull(responseJsonObject, REDIRECT_URIS.toString(), client.getRedirectUris());
Util.addToJSONObjectIfNotNull(responseJsonObject, RESPONSE_TYPES.toString(), ResponseType.toStringArray(client.getResponseTypes()));
Util.addToJSONObjectIfNotNull(responseJsonObject, GRANT_TYPES.toString(), client.getGrantTypes());
Util.addToJSONObjectIfNotNull(responseJsonObject, APPLICATION_TYPE.toString(), client.getApplicationType());
Util.addToJSONObjectIfNotNull(responseJsonObject, CONTACTS.toString(), client.getContacts());
Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_NAME.toString(), client.getClientName());
Util.addToJSONObjectIfNotNull(responseJsonObject, LOGO_URI.toString(), client.getLogoUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, CLIENT_URI.toString(), client.getClientUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, POLICY_URI.toString(), client.getPolicyUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, TOS_URI.toString(), client.getTosUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, JWKS_URI.toString(), client.getJwksUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, JWKS.toString(), client.getJwks());
Util.addToJSONObjectIfNotNull(responseJsonObject, SECTOR_IDENTIFIER_URI.toString(), client.getSectorIdentifierUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, SUBJECT_TYPE.toString(), client.getSubjectType());
Util.addToJSONObjectIfNotNull(responseJsonObject, ID_TOKEN_SIGNED_RESPONSE_ALG.toString(), client.getIdTokenSignedResponseAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, ID_TOKEN_ENCRYPTED_RESPONSE_ALG.toString(), client.getIdTokenEncryptedResponseAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, ID_TOKEN_ENCRYPTED_RESPONSE_ENC.toString(), client.getIdTokenEncryptedResponseEnc());
Util.addToJSONObjectIfNotNull(responseJsonObject, USERINFO_SIGNED_RESPONSE_ALG.toString(), client.getUserInfoSignedResponseAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, USERINFO_ENCRYPTED_RESPONSE_ALG.toString(), client.getUserInfoEncryptedResponseAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, USERINFO_ENCRYPTED_RESPONSE_ENC.toString(), client.getUserInfoEncryptedResponseEnc());
Util.addToJSONObjectIfNotNull(responseJsonObject, REQUEST_OBJECT_SIGNING_ALG.toString(), client.getRequestObjectSigningAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, REQUEST_OBJECT_ENCRYPTION_ALG.toString(), client.getRequestObjectEncryptionAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, REQUEST_OBJECT_ENCRYPTION_ENC.toString(), client.getRequestObjectEncryptionEnc());
Util.addToJSONObjectIfNotNull(responseJsonObject, TOKEN_ENDPOINT_AUTH_METHOD.toString(), client.getTokenEndpointAuthMethod());
Util.addToJSONObjectIfNotNull(responseJsonObject, TOKEN_ENDPOINT_AUTH_SIGNING_ALG.toString(), client.getTokenEndpointAuthSigningAlg());
Util.addToJSONObjectIfNotNull(responseJsonObject, DEFAULT_MAX_AGE.toString(), client.getDefaultMaxAge());
Util.addToJSONObjectIfNotNull(responseJsonObject, REQUIRE_AUTH_TIME.toString(), client.getRequireAuthTime());
Util.addToJSONObjectIfNotNull(responseJsonObject, DEFAULT_ACR_VALUES.toString(), client.getDefaultAcrValues());
Util.addToJSONObjectIfNotNull(responseJsonObject, INITIATE_LOGIN_URI.toString(), client.getInitiateLoginUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, POST_LOGOUT_REDIRECT_URIS.toString(), client.getPostLogoutRedirectUris());
Util.addToJSONObjectIfNotNull(responseJsonObject, REQUEST_URIS.toString(), client.getRequestUris());
// Logout params
Util.addToJSONObjectIfNotNull(responseJsonObject, FRONT_CHANNEL_LOGOUT_URI.toString(), client.getFrontChannelLogoutUri());
Util.addToJSONObjectIfNotNull(responseJsonObject, FRONT_CHANNEL_LOGOUT_SESSION_REQUIRED.toString(), client.getFrontChannelLogoutSessionRequired());
// Custom Params
String[] scopeNames = null;
String[] scopeDns = client.getScopes();
if (scopeDns != null) {
scopeNames = new String[scopeDns.length];
for (int i = 0; i < scopeDns.length; i++) {
Scope scope = scopeService.getScopeByDn(scopeDns[i]);
scopeNames[i] = scope.getDisplayName();
}
}
Util.addToJSONObjectIfNotNull(responseJsonObject, "scopes", scopeNames);
return responseJsonObject;
}
/**
* Puts custom object class and custom attributes in client object for persistence.
*
* @param p_client client object
* @param p_requestObject request object
*/
private void putCustomStuffIntoObject(Client p_client, JSONObject p_requestObject) throws JSONException {
// custom object class
final String customOC = appConfiguration.getDynamicRegistrationCustomObjectClass();
if (StringUtils.isNotBlank(customOC)) {
p_client.setCustomObjectClasses(new String[]{customOC});
}
// custom attributes (custom attributes must be in custom object class)
final List<String> attrList = appConfiguration.getDynamicRegistrationCustomAttributes();
if (attrList != null && !attrList.isEmpty()) {
for (String attr : attrList) {
if (p_requestObject.has(attr)) {
final JSONArray parameterValuesJsonArray = p_requestObject.optJSONArray(attr);
final List<String> parameterValues = parameterValuesJsonArray != null ?
toList(parameterValuesJsonArray) :
Arrays.asList(p_requestObject.getString(attr));
if (parameterValues != null && !parameterValues.isEmpty()) {
try {
boolean processed = processApplicationAttributes(p_client, attr, parameterValues);
if (!processed) {
p_client.getCustomAttributes().add(new CustomAttribute(attr, parameterValues));
}
} catch (Exception e) {
log.debug(e.getMessage(), e);
}
}
}
}
}
}
private boolean processApplicationAttributes(Client p_client, String attr, final List<String> parameterValues) {
if (StringHelper.equalsIgnoreCase("oxAuthTrustedClient", attr)) {
boolean trustedClient = StringHelper.toBoolean(parameterValues.get(0), false);
p_client.setTrustedClient(trustedClient);
return true;
}
return false;
}
private String clientScopesToString(Client client){
String[] scopeDns = client.getScopes();
if (scopeDns != null) {
String[] scopeNames = new String[scopeDns.length];
for (int i = 0; i < scopeDns.length; i++) {
Scope scope = scopeService.getScopeByDn(scopeDns[i]);
scopeNames[i] = scope.getDisplayName();
}
return StringUtils.join(scopeNames, " ");
}
return null;
}
}