package com.github.hburgmeier.jerseyoauth2.authsrv.impl.services;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import javax.inject.Inject;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.github.hburgmeier.jerseyoauth2.api.protocol.IAuthorizationRequest;
import com.github.hburgmeier.jerseyoauth2.api.protocol.IRequestFactory;
import com.github.hburgmeier.jerseyoauth2.api.protocol.OAuth2ErrorCode;
import com.github.hburgmeier.jerseyoauth2.api.protocol.OAuth2ParseException;
import com.github.hburgmeier.jerseyoauth2.api.protocol.OAuth2ProtocolException;
import com.github.hburgmeier.jerseyoauth2.api.protocol.ResponseBuilderException;
import com.github.hburgmeier.jerseyoauth2.api.types.ResponseType;
import com.github.hburgmeier.jerseyoauth2.api.user.IUser;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.IConfiguration;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.authorization.IAuthorizationService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.ClientServiceException;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.ClientType;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IAuthorizedClientApp;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IClientService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IPendingClientToken;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.client.IRegisteredClientApp;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.protocol.IOAuth2Response;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.protocol.IResponseBuilder;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.token.ITokenService;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.ui.AuthorizationFlowException;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.ui.IAuthorizationFlow;
import com.github.hburgmeier.jerseyoauth2.authsrv.api.user.IUserService;
import com.github.hburgmeier.jerseyoauth2.authsrv.impl.protocol.ClientIdentityValidator;
import com.github.hburgmeier.jerseyoauth2.authsrv.impl.protocol.InvalidScopeException;
import com.github.hburgmeier.jerseyoauth2.authsrv.impl.protocol.ScopeValidator;
import com.github.hburgmeier.jerseyoauth2.protocol.impl.HttpRequestAdapter;
public class AuthorizationService implements IAuthorizationService {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizationService.class);
private final IClientService clientService;
private final IUserService userService;
private final IAuthorizationFlow authFlow;
private final ITokenService tokenService;
private final IRequestFactory requestFactory;
private final IConfiguration configuration;
private final IResponseBuilder responseBuilder;
private final ScopeValidator scopeValidator;
private final ClientIdentityValidator clientIdValidator = new ClientIdentityValidator();
private final Set<String> defaultScopes;
@Inject
public AuthorizationService(final IClientService clientService, final IUserService userService,
final IAuthorizationFlow authFlow, final IConfiguration configuration, final ITokenService tokenService,
final IRequestFactory requestFactory, final IResponseBuilder responseBuilder)
{
this.clientService = clientService;
this.userService = userService;
this.authFlow = authFlow;
this.configuration = configuration;
this.tokenService = tokenService;
this.requestFactory = requestFactory;
this.responseBuilder = responseBuilder;
Set<String> defScopes = configuration.getDefaultScopes();
this.defaultScopes = defScopes==null?Collections.<String>emptySet():defScopes;
this.scopeValidator = new ScopeValidator(configuration);
}
@Override
public IOAuth2Response evaluateAuthorizationRequest(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) throws AuthorizationFlowException, IOException, ServletException, ResponseBuilderException {
try {
IRegisteredClientApp regClientApp = null;
IAuthorizationRequest oauthRequest = null;
try {
oauthRequest = requestFactory.parseAuthorizationRequest(new HttpRequestAdapter(request),
configuration.getEnableAuthorizationHeaderForClientAuth());
LOGGER.debug("Parsing of AuthzRequest successful");
IUser user = userService.getCurrentUser(request);
if (user == null) {
throw new InvalidUserException();
}
regClientApp = clientService.getRegisteredClient(oauthRequest.getClientId());
if (regClientApp == null) {
throw new InvalidClientException();
}
Set<String> scopes = oauthRequest.getScopes();
if (scopes==null || scopes.isEmpty()) {
LOGGER.warn("using default scopes");
scopes = defaultScopes;
}
try {
scopeValidator.validateScopes(scopes);
} catch (InvalidScopeException e)
{
LOGGER.error("Scope {} is unknown", e.getScope());
throw new OAuth2ProtocolException(OAuth2ErrorCode.INVALID_SCOPE, oauthRequest.getState(), "Scope is invalid", e);
}
LOGGER.debug("Response Type {}", oauthRequest.getResponseType());
ResponseType reqResponseType = oauthRequest.getResponseType();
switch (reqResponseType)
{
case CODE:
validateCodeRequest(oauthRequest, regClientApp);
break;
case TOKEN:
validateTokenRequest(oauthRequest, regClientApp);
break;
}
IAuthorizedClientApp authorizedClientApp = clientService.isAuthorized(user, regClientApp.getClientId(),
scopes);
if (authorizedClientApp != null) {
LOGGER.debug("client is already authorized");
return sendAuthorizationResponse(request, oauthRequest, regClientApp, authorizedClientApp);
} else {
LOGGER.debug("client is not authorized or missing scopes {}", scopes);
return authFlow.startAuthorizationFlow(user, regClientApp, scopes, oauthRequest, request);
}
} catch (InvalidUserException e) {
LOGGER.error("Missing or invalid user");
return authFlow.handleMissingUser(request);
} catch (OAuth2ParseException e) {
LOGGER.error("Problem with OAuth2 protocol", e);
URI redirectUrl = getRedirectUri(regClientApp, oauthRequest, true);
return sendErrorResponse(e, redirectUrl);
} catch (OAuth2ProtocolException e) {
LOGGER.error("Problem with OAuth2 protocol", e);
URI redirectUrl = getRedirectUri(regClientApp, oauthRequest, true);
return sendErrorResponse(e, redirectUrl);
}
} catch (InvalidClientException e) {
LOGGER.error("Problem with the redirect Url", e);
return authFlow.handleInvalidClient(request);
}
}
/*
@Override
public void sendAuthorizationReponse(HttpServletRequest request, HttpServletResponse response,
ResponseType reqResponseType, IRegisteredClientApp regClientApp, IAuthorizedClientApp authorizedClientApp,
String state)
throws OAuth2ProtocolException, IOException, ResponseBuilderException {
try {
if (reqResponseType.equals(ResponseType.CODE)) {
IPendingClientToken pendingClientToken = clientService
.createPendingClientToken(authorizedClientApp);
sendAuthorizationReponse(request, response, pendingClientToken, regClientApp, state);
} else {
LOGGER.debug("issue new token for token response type");
tokenService.issueNewToken(request, response, authorizedClientApp, reqResponseType, state);
}
} catch (ClientServiceException e) {
throw new OAuth2ProtocolException(OAuth2ErrorCode.SERVER_ERROR, "client is invalid", state, e);
}
}
*/
@Override
public IOAuth2Response sendAuthorizationResponse(HttpServletRequest request,
IAuthorizationRequest originalRequest, IRegisteredClientApp regClientApp,
IAuthorizedClientApp authorizedClientApp) throws IOException, OAuth2ProtocolException,
ResponseBuilderException {
try {
if (originalRequest.getResponseType() == ResponseType.CODE) {
IPendingClientToken pendingClientToken = clientService
.createPendingClientToken(authorizedClientApp);
return sendAuthorizationResponse(request, pendingClientToken, regClientApp, originalRequest.getState());
} else {
LOGGER.debug("issue new token for token response type");
return tokenService.issueNewToken(request, authorizedClientApp, originalRequest.getResponseType(), originalRequest.getState());
}
} catch (ClientServiceException e) {
throw new OAuth2ProtocolException(OAuth2ErrorCode.SERVER_ERROR, "client is invalid", originalRequest.getState(), e);
}
}
@Override
public IOAuth2Response sendErrorResponse(OAuth2ProtocolException ex,
URI redirectUrl) throws ResponseBuilderException {
return responseBuilder.buildAuthorizationRequestErrorResponse(ex, redirectUrl);
}
protected IOAuth2Response sendAuthorizationResponse(HttpServletRequest request,
IPendingClientToken clientAuth, IRegisteredClientApp clientApp, String state) throws ResponseBuilderException {
try {
URI redirectUrl = new URI(clientApp.getCallbackUrl());
return responseBuilder.buildAuthorizationCodeResponse(clientAuth.getCode(), redirectUrl , state);
} catch (URISyntaxException e) {
throw new ResponseBuilderException(e);
}
}
protected URI getRedirectUri(IRegisteredClientApp regClientApp, IAuthorizationRequest oauthRequest, boolean error) throws InvalidClientException
{
String result = null;
if (oauthRequest!=null && oauthRequest.getRedirectURI()!=null)
{
result = oauthRequest.getRedirectURI();
}
if (regClientApp!=null)
{
if (result!=null && regClientApp.getCallbackUrl()!=null)
{
if (!error) {
throw new InvalidClientException();
}
else {
result = regClientApp.getCallbackUrl();
}
}
if (result == null) {
result = regClientApp.getCallbackUrl();
}
}
if (result == null)
{
throw new InvalidClientException();
} else
{
try {
return new URI(result);
} catch (URISyntaxException e) {
throw new InvalidClientException(e);
}
}
}
protected void validateCodeRequest(IAuthorizationRequest oauthRequest, IRegisteredClientApp regClientApp) throws OAuth2ProtocolException
{
EnumSet<ClientType> allowedClientTypes = configuration.getAllowedClientTypesForAuthorizationCode();
if (!allowedClientTypes.contains(regClientApp.getClientType()))
{
throw new OAuth2ProtocolException(OAuth2ErrorCode.UNSUPPORTED_RESPONSE_TYPE, "Client type is allowed for Authorization Code.",
oauthRequest.getState());
}
clientIdValidator.validateAuthorizationRequest(oauthRequest, regClientApp);
}
protected void validateTokenRequest(IAuthorizationRequest oauthRequest, IRegisteredClientApp regClientApp) throws OAuth2ProtocolException
{
EnumSet<ClientType> allowedClientTypes = configuration.getAllowedClientTypesForImplicitGrant();
if (!allowedClientTypes.contains(regClientApp.getClientType())) {
throw new OAuth2ProtocolException(OAuth2ErrorCode.UNSUPPORTED_RESPONSE_TYPE, "Client type is allowed for Implicit Grant.", oauthRequest.getState());
}
if (!oauthRequest.getRedirectURI().equals(regClientApp.getCallbackUrl())) {
throw new OAuth2ProtocolException(OAuth2ErrorCode.INVALID_CLIENT, "Redirect uri does not match", oauthRequest.getState());
}
clientIdValidator.validateAuthorizationRequest(oauthRequest, regClientApp);
}
}