@Test
  public void acceptInvitationWithInvalidRedirectUri() throws Exception {
    ScimUser user = new ScimUser("user-id-001", "*****@*****.**", "first", "last");
    user.setOrigin(UAA);
    BaseClientDetails clientDetails =
        new BaseClientDetails("client-id", null, null, null, null, "http://example.com/redirect");
    when(scimUserProvisioning.verifyUser(anyString(), anyInt())).thenReturn(user);
    when(scimUserProvisioning.update(anyString(), anyObject())).thenReturn(user);
    when(scimUserProvisioning.retrieve(eq("user-id-001"))).thenReturn(user);
    when(clientDetailsService.loadClientByClientId("acmeClientId")).thenReturn(clientDetails);
    Map<String, String> userData = new HashMap<>();
    userData.put(USER_ID, "user-id-001");
    userData.put(EMAIL, "*****@*****.**");
    userData.put(REDIRECT_URI, "http://someother/redirect");
    userData.put(CLIENT_ID, "acmeClientId");
    when(expiringCodeStore.retrieveCode(anyString()))
        .thenReturn(
            new ExpiringCode(
                "code",
                new Timestamp(System.currentTimeMillis()),
                JsonUtils.writeValueAsString(userData)));

    String redirectLocation =
        emailInvitationsService.acceptInvitation("code", "password").getRedirectUri();

    verify(scimUserProvisioning).verifyUser(user.getId(), user.getVersion());
    verify(scimUserProvisioning).changePassword(user.getId(), null, "password");
    assertEquals("/home", redirectLocation);
  }
Example #2
0
 /**
  * Is a refresh token supported for this client (or the global setting if {@link
  * #setClientDetailsService(ClientDetailsService) clientDetailsService} is not set.
  *
  * @param authorizationRequest the current authorization request
  * @return boolean to indicate if refresh token is supported
  */
 protected boolean isSupportRefreshToken(OAuth2Request authorizationRequest) {
   if (clientDetailsService != null) {
     ClientDetails client =
         clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
     return client.getAuthorizedGrantTypes().contains("refresh_token");
   }
   return this.supportRefreshToken;
 }
  @Override
  public AuthorizationRequest createAuthorizationRequest(Map<String, String> parameters) {

    String clientId = parameters.get("client_id");
    if (clientId == null) {
      throw new InvalidClientException("A client id must be provided");
    }
    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);

    String requestNonce = parameters.get("nonce");

    // Only process if the user is authenticated. If the user is not authenticated yet, this
    // code will be called a second time once the user is redirected from the login page back
    // to the auth endpoint.
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

    if (requestNonce != null && principal != null && principal instanceof User) {

      // Check request nonce for reuse
      Collection<Nonce> clientNonces = nonceService.getByClientId(client.getClientId());
      for (Nonce nonce : clientNonces) {
        String nonceVal = nonce.getValue();
        if (nonceVal.equals(requestNonce)) {
          throw new NonceReuseException(client.getClientId(), nonce);
        }
      }

      // Store nonce
      Nonce nonce = new Nonce();
      nonce.setClientId(client.getClientId());
      nonce.setValue(requestNonce);
      DateTime now = new DateTime(new Date());
      nonce.setUseDate(now.toDate());
      DateTime expDate = now.plus(nonceStorageDuration);
      Date expirationJdkDate = expDate.toDate();
      nonce.setExpireDate(expirationJdkDate);

      nonceService.save(nonce);
    }

    Set<String> scopes = OAuth2Utils.parseParameterList(parameters.get("scope"));
    if ((scopes == null || scopes.isEmpty())) {
      // TODO: do we want to allow default scoping at all?
      // If no scopes are specified in the incoming data, it is possible to default to the client's
      // registered scopes, but minus the "openid" scope. OpenID Connect requests MUST have the
      // "openid" scope.
      Set<String> clientScopes = client.getScope();
      if (clientScopes.contains("openid")) {
        clientScopes.remove("openid");
      }
      scopes = clientScopes;
    }
    DefaultAuthorizationRequest request =
        new DefaultAuthorizationRequest(
            parameters, Collections.<String, String>emptyMap(), clientId, scopes);
    request.addClientDetails(client);
    return request;
  }
  /**
   * Create an authorization request applying various UAA rules to the authorizationParameters and
   * the registered client details.
   *
   * <ul>
   *   <li>For client_credentials grants, the default scopes are the client's granted authorities
   *   <li>For other grant types the default scopes are the registered scopes in the client details
   *   <li>Only scopes in those lists are valid, otherwise there is an exception
   *   <li>If the scopes contain separators then resource ids are extracted as the scope value up to
   *       the last index of the separator
   *   <li>Some scopes can be hard-wired to resource ids (like the open id connect values), in which
   *       case the separator is ignored
   * </ul>
   *
   * @see
   *     org.springframework.security.oauth2.provider.AuthorizationRequestFactory#createAuthorizationRequest(java.util.Map,
   *     java.lang.String, java.lang.String, java.util.Set)
   */
  @Override
  public AuthorizationRequest createAuthorizationRequest(
      Map<String, String> authorizationParameters) {

    String clientId = authorizationParameters.get("client_id");
    BaseClientDetails clientDetails =
        new BaseClientDetails(clientDetailsService.loadClientByClientId(clientId));

    Set<String> scopes = OAuth2Utils.parseParameterList(authorizationParameters.get("scope"));
    String grantType = authorizationParameters.get("grant_type");
    if ((scopes == null || scopes.isEmpty())) {
      if ("client_credentials".equals(grantType)) {
        // The client authorities should be a list of scopes
        scopes = AuthorityUtils.authorityListToSet(clientDetails.getAuthorities());
      } else {
        // The default for a user token is the scopes registered with
        // the client
        scopes = clientDetails.getScope();
      }
    }

    Set<String> scopesFromExternalAuthorities = null;
    if (!"client_credentials".equals(grantType) && securityContextAccessor.isUser()) {
      scopes = checkUserScopes(scopes, securityContextAccessor.getAuthorities(), clientDetails);

      // TODO: will the grantType ever contain client_credentials or
      // authorization_code
      // External Authorities are things like LDAP groups that will be
      // mapped to Oauth scopes
      // Add those scopes to the request. These scopes will not be
      // validated against the scopes
      // registered to a client.
      // These scopes also do not need approval. The fact that they are
      // already in an external
      // group communicates user approval. Denying approval does not mean
      // much
      scopesFromExternalAuthorities =
          findScopesFromAuthorities(authorizationParameters.get("authorities"));
    }

    Set<String> resourceIds = getResourceIds(clientDetails, scopes);
    clientDetails.setResourceIds(resourceIds);
    DefaultAuthorizationRequest request = new DefaultAuthorizationRequest(authorizationParameters);
    if (!scopes.isEmpty()) {
      request.setScope(scopes);
    }
    if (scopesFromExternalAuthorities != null) {
      Map<String, String> existingAuthorizationParameters = new LinkedHashMap<String, String>();
      existingAuthorizationParameters.putAll(request.getAuthorizationParameters());
      existingAuthorizationParameters.put(
          "external_scopes", OAuth2Utils.formatParameterList(scopesFromExternalAuthorities));
      request.setAuthorizationParameters(existingAuthorizationParameters);
    }

    request.addClientDetails(clientDetails);

    return request;
  }
Example #5
0
 @RequestMapping(value = "/oauth/clients/{client}", method = RequestMethod.GET)
 @ResponseBody
 public ClientDetails getClientDetails(@PathVariable String client) throws Exception {
   try {
     return removeSecret(clientDetailsService.loadClientByClientId(client));
   } catch (InvalidClientException e) {
     throw new NoSuchClientException("No such client: " + client);
   }
 }
Example #6
0
 /**
  * The refresh token validity period in seconds
  *
  * @param authorizationRequest the current authorization request
  * @return the refresh token validity period in seconds
  */
 protected int getRefreshTokenValiditySeconds(OAuth2Request authorizationRequest) {
   if (clientDetailsService != null) {
     ClientDetails client =
         clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
     Integer validity = client.getRefreshTokenValiditySeconds();
     if (validity != null) {
       return validity;
     }
   }
   return refreshTokenValiditySeconds;
 }
  @RequestMapping("/oauth/confirm_access")
  public ModelAndView getAccessConfirmation(@ModelAttribute AuthorizationRequest clientAuth)
      throws Exception {

    ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());

    TreeMap<String, Object> model = new TreeMap<String, Object>();
    model.put("auth_request", clientAuth);
    model.put("client", client);

    return new ModelAndView("access_confirmation", model);
  }
 @Test
 public void testDefaultConfiguration() {
   this.context = new AnnotationConfigEmbeddedWebApplicationContext();
   this.context.register(
       AuthorizationAndResourceServerConfiguration.class, MinimalSecureWebApplication.class);
   this.context.refresh();
   this.context.getBean(AUTHORIZATION_SERVER_CONFIG);
   this.context.getBean(RESOURCE_SERVER_CONFIG);
   this.context.getBean(OAuth2MethodSecurityConfiguration.class);
   ClientDetails config = this.context.getBean(BaseClientDetails.class);
   AuthorizationEndpoint endpoint = this.context.getBean(AuthorizationEndpoint.class);
   UserApprovalHandler handler =
       (UserApprovalHandler) ReflectionTestUtils.getField(endpoint, "userApprovalHandler");
   ClientDetailsService clientDetailsService = this.context.getBean(ClientDetailsService.class);
   ClientDetails clientDetails = clientDetailsService.loadClientByClientId(config.getClientId());
   assertThat(AopUtils.isJdkDynamicProxy(clientDetailsService), equalTo(true));
   assertThat(
       AopUtils.getTargetClass(clientDetailsService).getName(),
       equalTo(ClientDetailsService.class.getName()));
   assertThat(handler instanceof ApprovalStoreUserApprovalHandler, equalTo(true));
   assertThat(clientDetails, equalTo(config));
   verifyAuthentication(config);
 }
Example #9
0
  @RequestMapping(value = "/oauth/clients/{client}/secret", method = RequestMethod.PUT)
  @ResponseStatus(HttpStatus.NO_CONTENT)
  public void changeSecret(@PathVariable String client, @RequestBody SecretChangeRequest change) {

    ClientDetails clientDetails;
    try {
      clientDetails = clientDetailsService.loadClientByClientId(client);
    } catch (InvalidClientException e) {
      throw new NoSuchClientException("No such client: " + client);
    }

    checkPasswordChangeIsAllowed(clientDetails, change.getOldSecret());

    clientRegistrationService.updateClientSecret(client, change.getSecret());
  }
Example #10
0
  @RequestMapping("/oauth/confirm_access")
  public String confirm(
      @ModelAttribute AuthorizationRequest clientAuth,
      Map<String, Object> model,
      final HttpServletRequest request)
      throws Exception {

    if (clientAuth == null) {
      model.put(
          "error",
          "No authorizatioun request is present, so we cannot confirm access (we don't know what you are asking for).");
      // response.sendError(HttpServletResponse.SC_BAD_REQUEST);
    } else {
      ClientDetails client = clientDetailsService.loadClientByClientId(clientAuth.getClientId());
      model.put("auth_request", clientAuth);
      model.put("client", client);
      model.put(
          "message",
          "To confirm or deny access POST to the following locations with the parameters requested.");
      Map<String, Object> options =
          new HashMap<String, Object>() {
            {
              put(
                  "confirm",
                  new HashMap<String, String>() {
                    {
                      put("location", getLocation(request, "oauth/authorize"));
                      put("path", getPath(request, "oauth/authorize"));
                      put("key", "user_oauth_approval");
                      put("value", "true");
                    }
                  });
              put(
                  "deny",
                  new HashMap<String, String>() {
                    {
                      put("location", getLocation(request, "oauth/authorize"));
                      put("path", getPath(request, "oauth/authorize"));
                      put("key", "user_oauth_approval");
                      put("value", "false");
                    }
                  });
            }
          };
      model.put("options", options);
    }
    return "access_confirmation";
  }
 @Test
 public void testValidCredentialsScopesForClientOnly() throws Exception {
   ClientDetailsEntity clientDetailsEntity = new ClientDetailsEntity();
   Set<ClientScopeEntity> scopes = new HashSet<ClientScopeEntity>(2);
   scopes.add(new ClientScopeEntity(ScopePathType.ORCID_PROFILE_CREATE.value()));
   clientDetailsEntity.setClientScopes(scopes);
   String orcid = "2875-8158-1475-6194";
   when(clientDetailsService.loadClientByClientId(orcid)).thenReturn(clientDetailsEntity);
   OrcidClientCredentialsChecker checker =
       new OrcidClientCredentialsChecker(clientDetailsService, oAuth2RequestFactory);
   Set<String> requestedScopes =
       new HashSet<String>(Arrays.asList(ScopePathType.READ_PUBLIC.value()));
   checker.validateCredentials(
       "client_credentials",
       new TokenRequest(
           Collections.<String, String>emptyMap(), orcid, requestedScopes, "client_credentials"));
 }
  // TODO: add cases for username no existing external user with username not email
  @Test
  public void accept_invitation_with_external_user_that_does_not_have_email_as_their_username() {
    String userId = "user-id-001";
    String email = "*****@*****.**";
    String actualUsername = "******";
    ScimUser userBeforeAccept = new ScimUser(userId, email, "first", "last");
    userBeforeAccept.setPrimaryEmail(email);
    userBeforeAccept.setOrigin(Origin.SAML);

    when(scimUserProvisioning.verifyUser(eq(userId), anyInt())).thenReturn(userBeforeAccept);
    when(scimUserProvisioning.retrieve(eq(userId))).thenReturn(userBeforeAccept);

    BaseClientDetails clientDetails =
        new BaseClientDetails("client-id", null, null, null, null, "http://example.com/redirect");
    when(clientDetailsService.loadClientByClientId("acmeClientId")).thenReturn(clientDetails);

    Map<String, String> userData = new HashMap<>();
    userData.put(USER_ID, userBeforeAccept.getId());
    userData.put(EMAIL, userBeforeAccept.getPrimaryEmail());
    userData.put(REDIRECT_URI, "http://someother/redirect");
    userData.put(CLIENT_ID, "acmeClientId");
    when(expiringCodeStore.retrieveCode(anyString()))
        .thenReturn(
            new ExpiringCode(
                "code",
                new Timestamp(System.currentTimeMillis()),
                JsonUtils.writeValueAsString(userData)));

    ScimUser userAfterAccept =
        new ScimUser(
            userId,
            actualUsername,
            userBeforeAccept.getGivenName(),
            userBeforeAccept.getFamilyName());
    userAfterAccept.setPrimaryEmail(email);

    when(scimUserProvisioning.verifyUser(eq(userId), anyInt())).thenReturn(userAfterAccept);

    ScimUser acceptedUser = emailInvitationsService.acceptInvitation("code", "password").getUser();
    assertEquals(userAfterAccept.getUserName(), acceptedUser.getUserName());
    assertEquals(userAfterAccept.getName(), acceptedUser.getName());
    assertEquals(userAfterAccept.getPrimaryEmail(), acceptedUser.getPrimaryEmail());

    verify(scimUserProvisioning).verifyUser(eq(userId), anyInt());
  }
  @Override
  public UserDetails loadUserByUsername(String clientId)
      throws UsernameNotFoundException, DataAccessException {

    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);

    String password = client.getClientSecret();
    boolean enabled = true;
    boolean accountNonExpired = true;
    boolean credentialsNonExpired = true;
    boolean accountNonLocked = true;
    List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
    GrantedAuthority roleClient = new SimpleGrantedAuthority("ROLE_CLIENT");
    authorities.add(roleClient);

    return new User(
        clientId,
        password,
        enabled,
        accountNonExpired,
        credentialsNonExpired,
        accountNonLocked,
        authorities);
  }
Example #14
0
  @RequestMapping(
      value = "/invite_users",
      method = RequestMethod.POST,
      consumes = "application/json")
  public ResponseEntity<InvitationsResponse> inviteUsers(
      @RequestBody InvitationsRequest invitations,
      @RequestParam(value = "client_id", required = false) String clientId,
      @RequestParam(value = "redirect_uri") String redirectUri) {

    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication instanceof OAuth2Authentication) {
      OAuth2Authentication oAuth2Authentication = (OAuth2Authentication) authentication;

      if (clientId == null) {
        clientId = oAuth2Authentication.getOAuth2Request().getClientId();
      }
    }

    InvitationsResponse invitationsResponse = new InvitationsResponse();

    DomainFilter filter = new DomainFilter();
    List<IdentityProvider> activeProviders =
        providers.retrieveActive(IdentityZoneHolder.get().getId());
    ClientDetails client = clients.loadClientByClientId(clientId);
    for (String email : invitations.getEmails()) {
      try {
        List<IdentityProvider> providers = filter.filter(activeProviders, client, email);
        if (providers.size() == 1) {
          ScimUser user = findOrCreateUser(email, providers.get(0).getOriginKey());

          String accountsUrl = UaaUrlUtils.getUaaUrl("/invitations/accept");

          Map<String, String> data = new HashMap<>();
          data.put(InvitationConstants.USER_ID, user.getId());
          data.put(InvitationConstants.EMAIL, user.getPrimaryEmail());
          data.put(CLIENT_ID, clientId);
          data.put(REDIRECT_URI, redirectUri);
          data.put(ORIGIN, user.getOrigin());
          Timestamp expiry =
              new Timestamp(
                  System.currentTimeMillis() + (INVITATION_EXPIRY_DAYS * 24 * 60 * 60 * 1000));
          ExpiringCode code =
              expiringCodeStore.generateCode(JsonUtils.writeValueAsString(data), expiry, null);

          String invitationLink = accountsUrl + "?code=" + code.getCode();
          try {
            URL inviteLink = new URL(invitationLink);
            invitationsResponse
                .getNewInvites()
                .add(
                    InvitationsResponse.success(
                        user.getPrimaryEmail(), user.getId(), user.getOrigin(), inviteLink));
          } catch (MalformedURLException mue) {
            invitationsResponse
                .getFailedInvites()
                .add(
                    InvitationsResponse.failure(
                        email,
                        "invitation.exception.url",
                        String.format("Malformed url", invitationLink)));
          }
        } else if (providers.size() == 0) {
          invitationsResponse
              .getFailedInvites()
              .add(
                  InvitationsResponse.failure(
                      email, "provider.non-existent", "No authentication provider found."));
        } else {
          invitationsResponse
              .getFailedInvites()
              .add(
                  InvitationsResponse.failure(
                      email, "provider.ambiguous", "Multiple authentication providers found."));
        }
      } catch (ScimResourceConflictException x) {
        invitationsResponse
            .getFailedInvites()
            .add(
                InvitationsResponse.failure(
                    email,
                    "user.ambiguous",
                    "Multiple users with the same origin matched to the email address."));
      } catch (UaaException uaae) {
        invitationsResponse
            .getFailedInvites()
            .add(InvitationsResponse.failure(email, "invitation.exception", uaae.getMessage()));
      }
    }
    return new ResponseEntity<>(invitationsResponse, HttpStatus.OK);
  }
 @Override
 public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
   return clients_.loadClientByClientId(clientId);
 }
  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
      throws IOException, ServletException {

    final boolean debug = logger.isDebugEnabled();
    final HttpServletRequest request = (HttpServletRequest) req;
    final HttpServletResponse response = (HttpServletResponse) res;

    try {
      Authentication credentials = extractCredentials(request);

      if (credentials != null) {

        if (debug) {
          logger.debug("Authentication credentials found for '" + credentials.getName() + "'");
        }

        Authentication authResult = authenticationManager.authenticate(credentials);

        if (debug) {
          logger.debug("Authentication success: " + authResult.getName());
        }

        Authentication requestingPrincipal = SecurityContextHolder.getContext().getAuthentication();
        if (requestingPrincipal == null) {
          throw new BadCredentialsException(
              "No client authentication found. Remember to put a filter upstream of the LoginAuthenticationFilter.");
        }

        String clientId = request.getParameter("client_id");
        if (null == clientId) {
          logger.error("No client_id in the request");
          throw new BadCredentialsException("No client_id in the request");
        }

        // Check that the client exists
        ClientDetails authenticatingClient = clientDetailsService.loadClientByClientId(clientId);
        if (authenticatingClient == null) {
          throw new BadCredentialsException("No client " + clientId + " found");
        }

        DefaultAuthorizationRequest authorizationRequest =
            new DefaultAuthorizationRequest(
                getSingleValueMap(request),
                null,
                authenticatingClient.getClientId(),
                getScope(request));
        if (requestingPrincipal.isAuthenticated()) {
          // Ensure the OAuth2Authentication is authenticated
          authorizationRequest.setApproved(true);
        }

        SecurityContextHolder.getContext()
            .setAuthentication(new OAuth2Authentication(authorizationRequest, authResult));

        onSuccessfulAuthentication(request, response, authResult);
      }

    } catch (AuthenticationException failed) {
      SecurityContextHolder.clearContext();

      if (debug) {
        logger.debug("Authentication request for failed: " + failed);
      }

      onUnsuccessfulAuthentication(request, response, failed);

      authenticationEntryPoint.commence(request, response, failed);

      return;
    }

    chain.doFilter(request, response);
  }