<commit_before>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.surfnet.oaaas.resource;

import com.sun.jersey.api.client.ClientResponse.Status;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.surfnet.oaaas.auth.AbstractAuthenticator;
import org.surfnet.oaaas.auth.AbstractUserConsentHandler;
import org.surfnet.oaaas.auth.OAuth2Validator;
import org.surfnet.oaaas.auth.OAuth2Validator.*;
import org.surfnet.oaaas.auth.ValidationResponseException;
import org.surfnet.oaaas.auth.principal.AuthenticatedPrincipal;
import org.surfnet.oaaas.auth.principal.UserPassCredentials;
import org.surfnet.oaaas.model.*;
import org.surfnet.oaaas.repository.AccessTokenRepository;
import org.surfnet.oaaas.repository.AuthorizationRequestRepository;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.UUID;

import static org.surfnet.oaaas.auth.OAuth2Validator.*;

/**
 * Resource for handling all calls related to tokens. It adheres to <a
 * href="http://tools.ietf.org/html/draft-ietf-oauth-v2"> the OAuth spec</a>.
 *
 */
@Named
@Path("/")
public class TokenResource {

  public static final String BASIC_REALM = "Basic realm=\"OAuth2 Secure\"";

  public static final String WWW_AUTHENTICATE = "WWW-Authenticate";

  @Inject
  private AuthorizationRequestRepository authorizationRequestRepository;

  @Inject
  private AccessTokenRepository accessTokenRepository;

  @Inject
  private OAuth2Validator oAuth2Validator;

  private static final Logger LOG = LoggerFactory.getLogger(TokenResource.class);

  @GET
  @Path("/authorize")
  public Response authorizeCallbackGet(@Context HttpServletRequest request) {
    return authorizeCallback(request);
  }

  /**
   * Entry point for the authorize call which needs to return an authorization
   * code or (implicit grant) an access token
   *
   * @param request
   *          the {@link HttpServletRequest}
   * @return Response the response
   */
  @POST
  @Produces(MediaType.TEXT_HTML)
  @Path("/authorize")
  public Response authorizeCallback(@Context HttpServletRequest request) {
    return doProcess(request);
  }

  /**
   * Called after the user has given consent
   *
   * @param request
   *          the {@link HttpServletRequest}
   * @return Response the response
   */
  @POST
  @Produces(MediaType.TEXT_HTML)
  @Path("/consent")
  public Response consentCallback(@Context HttpServletRequest request) {
    return doProcess(request);
  }

  private Response doProcess(HttpServletRequest request) {
    AuthorizationRequest authReq = findAuthorizationRequest(request);
    if (authReq == null) {
      return serverError("Not a valid AbstractAuthenticator.AUTH_STATE on the Request");
    }
    processScopes(authReq, request);
    if (authReq.getResponseType().equals(OAuth2Validator.IMPLICIT_GRANT_RESPONSE_TYPE)) {
      AccessToken token = createAccessToken(authReq, true);
      return sendImplicitGrantResponse(authReq, token);
    } else {
      return sendAuthorizationCodeResponse(authReq);
    }
  }

  /*
   * In the user consent filter the scopes are (possible) set on the Request
   */
  private void processScopes(AuthorizationRequest authReq, HttpServletRequest request) {
    if (authReq.getClient().isSkipConsent()) {
      // return the scopes in the authentication request since the requested scopes are stored in the
      // authorizationRequest.
      authReq.setGrantedScopes(authReq.getRequestedScopes());
    } else {
      String[] scopes = (String[]) request.getAttribute(AbstractUserConsentHandler.GRANTED_SCOPES);
      if (!ArrayUtils.isEmpty(scopes)) {
        authReq.setGrantedScopes(Arrays.asList(scopes));
      } else {
        authReq.setGrantedScopes(null);
      }
    }
  }

  private AccessToken createAccessToken(AuthorizationRequest request, boolean isImplicitGrant) {
    Client client = request.getClient();
    long expireDuration = client.getExpireDuration();
    long expires = (expireDuration == 0L ? 0L : (System.currentTimeMillis() + (1000 * expireDuration)));
    String refreshToken = (client.isUseRefreshTokens() && !isImplicitGrant) ? getTokenValue(true) : null;
    AuthenticatedPrincipal principal = request.getPrincipal();
    AccessToken token = new AccessToken(getTokenValue(false), principal, client, expires, request.getGrantedScopes(), refreshToken);
    return accessTokenRepository.save(token);
  }

  private AuthorizationRequest findAuthorizationRequest(HttpServletRequest request) {
    String authState = (String) request.getAttribute(AbstractAuthenticator.AUTH_STATE);
    return authorizationRequestRepository.findByAuthState(authState);
  }

  @POST
  @Path("/token")
  @Produces(MediaType.APPLICATION_JSON)
  @Consumes("application/x-www-form-urlencoded")
  public Response token(@HeaderParam("Authorization") String authorization, final MultivaluedMap<String, String> formParameters) {
    AccessTokenRequest accessTokenRequest = AccessTokenRequest.fromMultiValuedFormParameters(formParameters);
    UserPassCredentials credentials = getUserPassCredentials(authorization, accessTokenRequest);
    String grantType = accessTokenRequest.getGrantType();
    if (GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) {
      accessTokenRequest.setClientId(credentials.getUsername());
    }
    ValidationResponse vr = oAuth2Validator.validate(accessTokenRequest);
    if (!vr.valid()) {
      return sendErrorResponse(vr);
    }
    AuthorizationRequest request;
    try {
      if (GRANT_TYPE_AUTHORIZATION_CODE.equals(grantType)) {
        request = authorizationCodeToken(accessTokenRequest);
      } else if (GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) {
        request = refreshTokenToken(accessTokenRequest);
      } else if (GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) {
        request =  new AuthorizationRequest();
        request.setClient(accessTokenRequest.getClient());
        // We have to construct a AuthenticatedPrincipal on-the-fly as there is only key-secret authentication
        request.setPrincipal(new AuthenticatedPrincipal(request.getClient().getClientId()));
        // Apply all client scopes to the access token.
        // TODO: take into account given scopes from the request
        request.setGrantedScopes(request.getClient().getScopes());
      }
      else {
        return sendErrorResponse(ValidationResponse.UNSUPPORTED_GRANT_TYPE);
      }
    } catch (ValidationResponseException e) {
      return sendErrorResponse(e.v);
    }
    if (!request.getClient().isExactMatch(credentials)) {
      return Response.status(Status.UNAUTHORIZED).header(WWW_AUTHENTICATE, BASIC_REALM).build();
    }
    AccessToken token = createAccessToken(request, false);

    AccessTokenResponse response = new AccessTokenResponse(token.getToken(), BEARER, token.getExpiresIn(), token.getRefreshToken(), StringUtils.join(token.getScopes(), ','));

    return Response
            .ok()
            .entity(response)
            .cacheControl(cacheControlNoStore())
            .header("Pragma", "no-cache")
            .build();

  }

  private CacheControl cacheControlNoStore() {
    CacheControl cacheControl = new CacheControl();
    cacheControl.setNoStore(true);
    return cacheControl;
  }

  private AuthorizationRequest authorizationCodeToken(AccessTokenRequest accessTokenRequest) {
    AuthorizationRequest authReq = authorizationRequestRepository.findByAuthorizationCode(accessTokenRequest.getCode());
    if (authReq == null) {
      throw new ValidationResponseException(ValidationResponse.INVALID_GRANT_AUTHORIZATION_CODE);
    }
    String uri = accessTokenRequest.getRedirectUri();
    if (!authReq.getRedirectUri().equalsIgnoreCase(uri)) {
      throw new ValidationResponseException(ValidationResponse.REDIRECT_URI_DIFFERENT);
    }<commit_msg>Return token-scopes separated by a space character, as per the spec.<commit_after>/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.surfnet.oaaas.resource;

import com.sun.jersey.api.client.ClientResponse.Status;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.surfnet.oaaas.auth.AbstractAuthenticator;
import org.surfnet.oaaas.auth.AbstractUserConsentHandler;
import org.surfnet.oaaas.auth.OAuth2Validator;
import org.surfnet.oaaas.auth.OAuth2Validator.*;
import org.surfnet.oaaas.auth.ValidationResponseException;
import org.surfnet.oaaas.auth.principal.AuthenticatedPrincipal;
import org.surfnet.oaaas.auth.principal.UserPassCredentials;
import org.surfnet.oaaas.model.*;
import org.surfnet.oaaas.repository.AccessTokenRepository;
import org.surfnet.oaaas.repository.AuthorizationRequestRepository;

import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.UUID;

import static org.surfnet.oaaas.auth.OAuth2Validator.*;

/**
 * Resource for handling all calls related to tokens. It adheres to <a
 * href="http://tools.ietf.org/html/draft-ietf-oauth-v2"> the OAuth spec</a>.
 *
 */
@Named
@Path("/")
public class TokenResource {

  public static final String BASIC_REALM = "Basic realm=\"OAuth2 Secure\"";

  public static final String WWW_AUTHENTICATE = "WWW-Authenticate";

  @Inject
  private AuthorizationRequestRepository authorizationRequestRepository;

  @Inject
  private AccessTokenRepository accessTokenRepository;

  @Inject
  private OAuth2Validator oAuth2Validator;

  private static final Logger LOG = LoggerFactory.getLogger(TokenResource.class);

  @GET
  @Path("/authorize")
  public Response authorizeCallbackGet(@Context HttpServletRequest request) {
    return authorizeCallback(request);
  }

  /**
   * Entry point for the authorize call which needs to return an authorization
   * code or (implicit grant) an access token
   *
   * @param request
   *          the {@link HttpServletRequest}
   * @return Response the response
   */
  @POST
  @Produces(MediaType.TEXT_HTML)
  @Path("/authorize")
  public Response authorizeCallback(@Context HttpServletRequest request) {
    return doProcess(request);
  }

  /**
   * Called after the user has given consent
   *
   * @param request
   *          the {@link HttpServletRequest}
   * @return Response the response
   */
  @POST
  @Produces(MediaType.TEXT_HTML)
  @Path("/consent")
  public Response consentCallback(@Context HttpServletRequest request) {
    return doProcess(request);
  }

  private Response doProcess(HttpServletRequest request) {
    AuthorizationRequest authReq = findAuthorizationRequest(request);
    if (authReq == null) {
      return serverError("Not a valid AbstractAuthenticator.AUTH_STATE on the Request");
    }
    processScopes(authReq, request);
    if (authReq.getResponseType().equals(OAuth2Validator.IMPLICIT_GRANT_RESPONSE_TYPE)) {
      AccessToken token = createAccessToken(authReq, true);
      return sendImplicitGrantResponse(authReq, token);
    } else {
      return sendAuthorizationCodeResponse(authReq);
    }
  }

  /*
   * In the user consent filter the scopes are (possible) set on the Request
   */
  private void processScopes(AuthorizationRequest authReq, HttpServletRequest request) {
    if (authReq.getClient().isSkipConsent()) {
      // return the scopes in the authentication request since the requested scopes are stored in the
      // authorizationRequest.
      authReq.setGrantedScopes(authReq.getRequestedScopes());
    } else {
      String[] scopes = (String[]) request.getAttribute(AbstractUserConsentHandler.GRANTED_SCOPES);
      if (!ArrayUtils.isEmpty(scopes)) {
        authReq.setGrantedScopes(Arrays.asList(scopes));
      } else {
        authReq.setGrantedScopes(null);
      }
    }
  }

  private AccessToken createAccessToken(AuthorizationRequest request, boolean isImplicitGrant) {
    Client client = request.getClient();
    long expireDuration = client.getExpireDuration();
    long expires = (expireDuration == 0L ? 0L : (System.currentTimeMillis() + (1000 * expireDuration)));
    String refreshToken = (client.isUseRefreshTokens() && !isImplicitGrant) ? getTokenValue(true) : null;
    AuthenticatedPrincipal principal = request.getPrincipal();
    AccessToken token = new AccessToken(getTokenValue(false), principal, client, expires, request.getGrantedScopes(), refreshToken);
    return accessTokenRepository.save(token);
  }

  private AuthorizationRequest findAuthorizationRequest(HttpServletRequest request) {
    String authState = (String) request.getAttribute(AbstractAuthenticator.AUTH_STATE);
    return authorizationRequestRepository.findByAuthState(authState);
  }

  @POST
  @Path("/token")
  @Produces(MediaType.APPLICATION_JSON)
  @Consumes("application/x-www-form-urlencoded")
  public Response token(@HeaderParam("Authorization") String authorization, final MultivaluedMap<String, String> formParameters) {
    AccessTokenRequest accessTokenRequest = AccessTokenRequest.fromMultiValuedFormParameters(formParameters);
    UserPassCredentials credentials = getUserPassCredentials(authorization, accessTokenRequest);
    String grantType = accessTokenRequest.getGrantType();
    if (GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) {
      accessTokenRequest.setClientId(credentials.getUsername());
    }
    ValidationResponse vr = oAuth2Validator.validate(accessTokenRequest);
    if (!vr.valid()) {
      return sendErrorResponse(vr);
    }
    AuthorizationRequest request;
    try {
      if (GRANT_TYPE_AUTHORIZATION_CODE.equals(grantType)) {
        request = authorizationCodeToken(accessTokenRequest);
      } else if (GRANT_TYPE_REFRESH_TOKEN.equals(grantType)) {
        request = refreshTokenToken(accessTokenRequest);
      } else if (GRANT_TYPE_CLIENT_CREDENTIALS.equals(grantType)) {
        request =  new AuthorizationRequest();
        request.setClient(accessTokenRequest.getClient());
        // We have to construct a AuthenticatedPrincipal on-the-fly as there is only key-secret authentication
        request.setPrincipal(new AuthenticatedPrincipal(request.getClient().getClientId()));
        // Apply all client scopes to the access token.
        // TODO: take into account given scopes from the request
        request.setGrantedScopes(request.getClient().getScopes());
      }
      else {
        return sendErrorResponse(ValidationResponse.UNSUPPORTED_GRANT_TYPE);
      }
    } catch (ValidationResponseException e) {
      return sendErrorResponse(e.v);
    }
    if (!request.getClient().isExactMatch(credentials)) {
      return Response.status(Status.UNAUTHORIZED).header(WWW_AUTHENTICATE, BASIC_REALM).build();
    }
    AccessToken token = createAccessToken(request, false);

    AccessTokenResponse response = new AccessTokenResponse(token.getToken(), BEARER, token.getExpiresIn(), token.getRefreshToken(), StringUtils.join(token.getScopes(), ' '));

    return Response
            .ok()
            .entity(response)
            .cacheControl(cacheControlNoStore())
            .header("Pragma", "no-cache")
            .build();

  }

  private CacheControl cacheControlNoStore() {
    CacheControl cacheControl = new CacheControl();
    cacheControl.setNoStore(true);
    return cacheControl;
  }

  private AuthorizationRequest authorizationCodeToken(AccessTokenRequest accessTokenRequest) {
    AuthorizationRequest authReq = authorizationRequestRepository.findByAuthorizationCode(accessTokenRequest.getCode());
    if (authReq == null) {
      throw new ValidationResponseException(ValidationResponse.INVALID_GRANT_AUTHORIZATION_CODE);
    }
    String uri = accessTokenRequest.getRedirectUri();
    if (!authReq.getRedirectUri().equalsIgnoreCase(uri)) {
      throw new ValidationResponseException(ValidationResponse.REDIRECT_URI_DIFFERENT);
    }
{'fraction_non_alphanumeric': 0.09360303699926341, 'fraction_numerical': 0.0023797382287948326, 'mean_word_length': 4.420761670761671, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3320247', 'n_tokens_mistral': 4935, 'n_tokens_neox': 4852, 'n_words': 1152}