/**
   * Delete the indicated client from the system.
   *
   * @param clientId
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + SystemScopeService.RESOURCE_TOKEN_SCOPE
          + "')")
  @RequestMapping(
      value = "/{id}",
      method = RequestMethod.DELETE,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public String deleteResource(
      @PathVariable("id") String clientId, Model m, OAuth2Authentication auth) {

    ClientDetailsEntity client = clientService.loadClientByClientId(clientId);

    if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) {

      clientService.deleteClient(client);

      m.addAttribute(HttpCodeView.CODE, HttpStatus.NO_CONTENT); // http 204

      return HttpCodeView.VIEWNAME;
    } else {
      // client mismatch
      logger.error(
          "readClientConfiguration failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); // http 403

      return HttpCodeView.VIEWNAME;
    }
  }
  /**
   * Delete the indicated client from the system.
   *
   * @param clientId
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + OAuth2AccessTokenEntity.REGISTRATION_TOKEN_SCOPE
          + "')")
  @RequestMapping(value = "/{id}", method = RequestMethod.DELETE, produces = "application/json")
  public String deleteClient(
      @PathVariable("id") String clientId, Model m, OAuth2Authentication auth) {

    ClientDetailsEntity client = clientService.loadClientByClientId(clientId);

    if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) {

      clientService.deleteClient(client);

      m.addAttribute("code", HttpStatus.NO_CONTENT); // http 204

      return "httpCodeView";
    } else {
      // client mismatch
      logger.error(
          "readClientConfiguration failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute("code", HttpStatus.FORBIDDEN); // http 403

      return "httpCodeView";
    }
  }
Esempio n. 3
0
 private ExpiringOAuth2RefreshToken createRefreshToken(OAuth2Authentication authentication) {
   if (!isSupportRefreshToken(authentication.getOAuth2Request())) {
     return null;
   }
   int validitySeconds = getRefreshTokenValiditySeconds(authentication.getOAuth2Request());
   ExpiringOAuth2RefreshToken refreshToken =
       new DefaultExpiringOAuth2RefreshToken(
           UUID.randomUUID().toString(),
           new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
   return refreshToken;
 }
Esempio n. 4
0
  private OAuth2AccessToken createAccessToken(
      OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
    DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());
    int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
    if (validitySeconds > 0) {
      token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
    }
    token.setRefreshToken(refreshToken);
    token.setScope(authentication.getOAuth2Request().getScope());

    return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
  }
  /**
   * Get the meta information for a client.
   *
   * @param clientId
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + SystemScopeService.RESOURCE_TOKEN_SCOPE
          + "')")
  @RequestMapping(
      value = "/{id}",
      method = RequestMethod.GET,
      produces = MediaType.APPLICATION_JSON_VALUE)
  public String readResourceConfiguration(
      @PathVariable("id") String clientId, Model m, OAuth2Authentication auth) {

    ClientDetailsEntity client = clientService.loadClientByClientId(clientId);

    if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) {

      try {
        // possibly update the token
        OAuth2AccessTokenEntity token = fetchValidRegistrationToken(auth, client);

        RegisteredClient registered =
            new RegisteredClient(
                client,
                token.getValue(),
                config.getIssuer()
                    + "resource/"
                    + UriUtils.encodePathSegment(client.getClientId(), "UTF-8"));

        // send it all out to the view
        m.addAttribute("client", registered);
        m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200

        return ClientInformationResponseView.VIEWNAME;
      } catch (UnsupportedEncodingException e) {
        logger.error("Unsupported encoding", e);
        m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR);
        return HttpCodeView.VIEWNAME;
      }
    } else {
      // client mismatch
      logger.error(
          "readResourceConfiguration failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); // http 403

      return HttpCodeView.VIEWNAME;
    }
  }
 @Override
 public OAuth2AccessToken getAccessToken(
     OAuth2ProtectedResourceDetails resource, Authentication authentication) {
   if (authentication instanceof OAuth2Authentication) {
     OAuth2AccessToken token = tokenStore.getAccessToken((OAuth2Authentication) authentication);
     if (token != null) {
       logger.debug("Found token for OAuth2Authentication");
       return token;
     }
   }
   Collection<OAuth2AccessToken> tokens = tokenStore.findTokensByClientId(resource.getClientId());
   if (tokens == null || tokens.isEmpty()) {
     return null;
   }
   Iterator<OAuth2AccessToken> iter = tokens.iterator();
   while (iter.hasNext()) {
     OAuth2AccessToken token = iter.next();
     OAuth2Authentication oauth2Auth = tokenStore.readAuthentication(token);
     if (oauth2Auth != null
         && resource.getClientId().equals(oauth2Auth.getOAuth2Request().getClientId())
         && oauth2Auth.getName().equals(authentication.getName())) {
       logger.debug("token for user: "******" found");
       return token;
     }
   }
   logger.debug("token not found");
   return null;
 }
  @RequestMapping(method = RequestMethod.GET, produces = MimeTypeUtils.APPLICATION_JSON_VALUE)
  public String listResourceSets(Model m, Authentication auth) {
    ensureOAuthScope(auth, SystemScopeService.UMA_PROTECTION_SCOPE);

    String owner = auth.getName();

    Collection<ResourceSet> resourceSets = Collections.emptySet();
    if (auth instanceof OAuth2Authentication) {
      // if it's an OAuth mediated call, it's on behalf of a client, so look that up too
      OAuth2Authentication o2a = (OAuth2Authentication) auth;
      resourceSets =
          resourceSetService.getAllForOwnerAndClient(owner, o2a.getOAuth2Request().getClientId());
    } else {
      // otherwise get everything for the current user
      resourceSets = resourceSetService.getAllForOwner(owner);
    }

    // build the entity here and send to the display

    Set<String> ids = new HashSet<>();
    for (ResourceSet resourceSet : resourceSets) {
      ids.add(
          resourceSet
              .getId()
              .toString()); // add them all as strings so that gson renders them properly
    }

    m.addAttribute(JsonEntityView.ENTITY, ids);
    return JsonEntityView.VIEWNAME;
  }
Esempio n. 8
0
 protected String extractClientIdFromAuthentication(Authentication authentication) {
   if (authentication == null) {
     return null;
   }
   if (authentication instanceof OAuth2Authentication) {
     OAuth2Authentication a = (OAuth2Authentication) authentication;
     return a.getOAuth2Request().getClientId();
   }
   return null;
 }
  public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
    String refreshToken = null;
    if (token.getRefreshToken() != null) {
      refreshToken = token.getRefreshToken().getValue();
    }

    // the JdbcTokenStore removes the existing token for this token_id [if it exists]
    // We'll avoid doing so for now, unless a compelling reason to do otherwise presents itself
    //        if (readAccessToken(token.getValue()) != null) {
    //            removeAccessToken(token.getValue());
    //        }

    Map<String, AttributeValueUpdate> updates = new HashMap<String, AttributeValueUpdate>();
    updates.put(
        schema.getAccessColumnToken(),
        new AttributeValueUpdate(
            new AttributeValue().withB(serializeAccessToken(token)), AttributeAction.PUT));
    DynamoDBUtils.nullSafeUpdateS(
        updates,
        schema.getAccessColumnAuthenticationId(),
        authenticationKeyGenerator.extractKey(authentication));
    if (authentication.isClientOnly()
        || authentication.getName() == null
        || authentication.getName().length() == 0) {
      DynamoDBUtils.nullSafeUpdateS(
          updates, schema.getAccessColumnUserName(), schema.getAccessNullUserToken());
      updates.put(
          schema.getAccessColumnIsNullUser(),
          new AttributeValueUpdate(
              new AttributeValue().withN(schema.getAccessIsNullUserTrueToken()),
              AttributeAction.PUT));
    } else {
      DynamoDBUtils.nullSafeUpdateS(
          updates, schema.getAccessColumnUserName(), authentication.getName());
      DynamoDBUtils.nullSafeUpdateS(updates, schema.getAccessColumnIsNullUser(), null);
    }

    DynamoDBUtils.nullSafeUpdateS(
        updates, schema.getAccessColumnClientId(), authentication.getOAuth2Request().getClientId());
    updates.put(
        schema.getAccessColumnAuthentication(),
        new AttributeValueUpdate(
            new AttributeValue().withB(serializeAuthentication(authentication)),
            AttributeAction.PUT));
    DynamoDBUtils.nullSafeUpdateS(
        updates, schema.getAccessColumnRefreshToken(), extractTokenKey(refreshToken));

    dynamoDBTemplate.update(
        schema.getAccessTableName(), //
        Collections.singletonMap(
            schema.getAccessColumnTokenId(),
            new AttributeValue(extractTokenKey(token.getValue()))), //
        updates);
  }
Esempio n. 10
0
 public String getClientId(String tokenValue) {
   OAuth2Authentication authentication = tokenStore.readAuthentication(tokenValue);
   if (authentication == null) {
     throw new InvalidTokenException("Invalid access token: " + tokenValue);
   }
   OAuth2Request authorizationRequest = authentication.getOAuth2Request();
   if (authorizationRequest == null) {
     throw new InvalidTokenException("Invalid access token (no client id): " + tokenValue);
   }
   return authorizationRequest.getClientId();
 }
  @RequestMapping(
      method = RequestMethod.POST,
      produces = MimeTypeUtils.APPLICATION_JSON_VALUE,
      consumes = MimeTypeUtils.APPLICATION_JSON_VALUE)
  public String createResourceSet(@RequestBody String jsonString, Model m, Authentication auth) {
    ensureOAuthScope(auth, SystemScopeService.UMA_PROTECTION_SCOPE);

    ResourceSet rs = parseResourceSet(jsonString);

    if (rs == null) { // there was no resource set in the body
      logger.warn("Resource set registration missing body.");

      m.addAttribute("code", HttpStatus.BAD_REQUEST);
      m.addAttribute("error_description", "Resource request was missing body.");
      return JsonErrorView.VIEWNAME;
    }

    if (auth instanceof OAuth2Authentication) {
      // if it's an OAuth mediated call, it's on behalf of a client, so store that
      OAuth2Authentication o2a = (OAuth2Authentication) auth;
      rs.setClientId(o2a.getOAuth2Request().getClientId());
      rs.setOwner(auth.getName()); // the username is going to be in the auth object
    } else {
      // this one shouldn't be called if it's not OAuth
      m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
      m.addAttribute(JsonErrorView.ERROR_MESSAGE, "This call must be made with an OAuth token");
      return JsonErrorView.VIEWNAME;
    }

    rs = validateScopes(rs);

    if (Strings.isNullOrEmpty(rs.getName()) // there was no name (required)
        || rs.getScopes() == null // there were no scopes (required)
    ) {

      logger.warn("Resource set registration missing one or more required fields.");

      m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST);
      m.addAttribute(
          JsonErrorView.ERROR_MESSAGE, "Resource request was missing one or more required fields.");
      return JsonErrorView.VIEWNAME;
    }

    ResourceSet saved = resourceSetService.saveNew(rs);

    m.addAttribute(HttpCodeView.CODE, HttpStatus.CREATED);
    m.addAttribute(JsonEntityView.ENTITY, saved);
    m.addAttribute(
        ResourceSetEntityAbbreviatedView.LOCATION, config.getIssuer() + URL + "/" + saved.getId());

    return ResourceSetEntityAbbreviatedView.VIEWNAME;
  }
  /**
   * Get the meta information for a client.
   *
   * @param clientId
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + OAuth2AccessTokenEntity.REGISTRATION_TOKEN_SCOPE
          + "')")
  @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = "application/json")
  public String readClientConfiguration(
      @PathVariable("id") String clientId, Model m, OAuth2Authentication auth) {

    ClientDetailsEntity client = clientService.loadClientByClientId(clientId);

    if (client != null && client.getClientId().equals(auth.getOAuth2Request().getClientId())) {

      // we return the token that we got in
      OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
      OAuth2AccessTokenEntity token = tokenService.readAccessToken(details.getTokenValue());

      // TODO: urlencode the client id for safety?
      RegisteredClient registered =
          new RegisteredClient(
              client, token.getValue(), config.getIssuer() + "register/" + client.getClientId());

      // send it all out to the view
      m.addAttribute("client", registered);
      m.addAttribute("code", HttpStatus.OK); // http 200

      return "clientInformationResponseView";
    } else {
      // client mismatch
      logger.error(
          "readClientConfiguration failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute("code", HttpStatus.FORBIDDEN); // http 403

      return "httpCodeView";
    }
  }
 @PersistenceConstructor
 public OAuth2AuthenticationAccessToken(
     OAuth2AccessToken oAuth2AccessToken,
     OAuth2Authentication authentication,
     String authenticationId) {
   this.id = UUID.randomUUID().toString();
   this.tokenId = oAuth2AccessToken.getValue();
   this.oAuth2AccessToken = oAuth2AccessToken;
   this.authenticationId = authenticationId;
   this.userName = authentication.getName();
   this.clientId = authentication.getOAuth2Request().getClientId();
   this.authentication = authentication;
   this.refreshToken = oAuth2AccessToken.getRefreshToken().getValue();
 }
Esempio n. 14
0
 /**
  * Create a refreshed authentication.
  *
  * @param authentication The authentication.
  * @param scope The scope for the refreshed token.
  * @return The refreshed authentication.
  * @throws InvalidScopeException If the scope requested is invalid or wider than the original
  *     scope.
  */
 private OAuth2Authentication createRefreshedAuthentication(
     OAuth2Authentication authentication, Set<String> scope) {
   OAuth2Authentication narrowed = authentication;
   if (scope != null && !scope.isEmpty()) {
     OAuth2Request clientAuth = authentication.getOAuth2Request();
     Set<String> originalScope = clientAuth.getScope();
     if (originalScope == null || !originalScope.containsAll(scope)) {
       throw new InvalidScopeException(
           "Unable to narrow the scope of the client authentication to " + scope + ".",
           originalScope);
     } else {
       narrowed = new OAuth2Authentication(clientAuth, authentication.getUserAuthentication());
     }
   }
   return narrowed;
 }
 private Collection<OAuth2AccessToken> enhance(Collection<OAuth2AccessToken> tokens) {
   Collection<OAuth2AccessToken> result = new ArrayList<OAuth2AccessToken>();
   for (OAuth2AccessToken prototype : tokens) {
     DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(prototype);
     OAuth2Authentication authentication = tokenStore.readAuthentication(token);
     if (authentication == null) {
       continue;
     }
     String clientId = authentication.getOAuth2Request().getClientId();
     if (clientId != null) {
       Map<String, Object> map = new HashMap<String, Object>(token.getAdditionalInformation());
       map.put("client_id", clientId);
       token.setAdditionalInformation(map);
       result.add(token);
     }
   }
   return result;
 }
Esempio n. 16
0
  public OAuth2AccessToken refreshAccessToken(
      String refreshTokenValue, AuthorizationRequest request) throws AuthenticationException {

    if (!supportRefreshToken) {
      throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2RefreshToken refreshToken = tokenStore.readRefreshToken(refreshTokenValue);
    if (refreshToken == null) {
      throw new InvalidGrantException("Invalid refresh token: " + refreshTokenValue);
    }

    OAuth2Authentication authentication =
        tokenStore.readAuthenticationForRefreshToken(refreshToken);
    String clientId = authentication.getOAuth2Request().getClientId();
    if (clientId == null || !clientId.equals(request.getClientId())) {
      throw new InvalidGrantException("Wrong client for this refresh token: " + refreshTokenValue);
    }

    // clear out any access tokens already associated with the refresh
    // token.
    tokenStore.removeAccessTokenUsingRefreshToken(refreshToken);

    if (isExpired(refreshToken)) {
      tokenStore.removeRefreshToken(refreshToken);
      throw new InvalidTokenException("Invalid refresh token (expired): " + refreshToken);
    }

    authentication = createRefreshedAuthentication(authentication, request.getScope());

    if (!reuseRefreshToken) {
      tokenStore.removeRefreshToken(refreshToken);
      refreshToken = createRefreshToken(authentication);
    }

    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    tokenStore.storeAccessToken(accessToken, authentication);
    if (!reuseRefreshToken) {
      tokenStore.storeRefreshToken(refreshToken, authentication);
    }
    return accessToken;
  }
  /**
   * Update the metainformation for a given client.
   *
   * @param clientId
   * @param jsonString
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + OAuth2AccessTokenEntity.REGISTRATION_TOKEN_SCOPE
          + "')")
  @RequestMapping(
      value = "/{id}",
      method = RequestMethod.PUT,
      produces = "application/json",
      consumes = "application/json")
  public String updateClient(
      @PathVariable("id") String clientId,
      @RequestBody String jsonString,
      Model m,
      OAuth2Authentication auth) {

    ClientDetailsEntity newClient = ClientDetailsEntityJsonProcessor.parse(jsonString);
    ClientDetailsEntity oldClient = clientService.loadClientByClientId(clientId);

    if (newClient != null
        && oldClient != null // we have an existing client and the new one parsed
        && oldClient
            .getClientId()
            .equals(
                auth.getOAuth2Request()
                    .getClientId()) // the client passed in the URI matches the one in the auth
        && oldClient
            .getClientId()
            .equals(
                newClient.getClientId()) // the client passed in the body matches the one in the URI
    ) {

      // a client can't ask to update its own client secret to any particular value
      newClient.setClientSecret(oldClient.getClientSecret());

      // we need to copy over all of the local and SECOAUTH fields
      newClient.setAccessTokenValiditySeconds(oldClient.getAccessTokenValiditySeconds());
      newClient.setIdTokenValiditySeconds(oldClient.getIdTokenValiditySeconds());
      newClient.setRefreshTokenValiditySeconds(oldClient.getRefreshTokenValiditySeconds());
      newClient.setDynamicallyRegistered(true); // it's still dynamically registered
      newClient.setAllowIntrospection(oldClient.isAllowIntrospection());
      newClient.setAuthorities(oldClient.getAuthorities());
      newClient.setClientDescription(oldClient.getClientDescription());
      newClient.setCreatedAt(oldClient.getCreatedAt());
      newClient.setReuseRefreshToken(oldClient.isReuseRefreshToken());

      // set of scopes that are OK for clients to dynamically register for
      Set<SystemScope> dynScopes = scopeService.getDynReg();

      // scopes that the client is asking for
      Set<SystemScope> requestedScopes = scopeService.fromStrings(newClient.getScope());

      // the scopes that the client can have must be a subset of the dynamically allowed scopes
      Set<SystemScope> allowedScopes = Sets.intersection(dynScopes, requestedScopes);

      // make sure that the client doesn't ask for scopes it can't have
      newClient.setScope(scopeService.toStrings(allowedScopes));

      try {
        // save the client
        ClientDetailsEntity savedClient = clientService.updateClient(oldClient, newClient);

        // we return the token that we got in
        // TODO: rotate this after some set amount of time
        OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
        OAuth2AccessTokenEntity token = tokenService.readAccessToken(details.getTokenValue());

        // TODO: urlencode the client id for safety?
        RegisteredClient registered =
            new RegisteredClient(
                savedClient,
                token.getValue(),
                config.getIssuer() + "register/" + savedClient.getClientId());

        // send it all out to the view
        m.addAttribute("client", registered);
        m.addAttribute("code", HttpStatus.OK); // http 200

        return "clientInformationResponseView";
      } catch (IllegalArgumentException e) {
        logger.error("Couldn't save client", e);
        m.addAttribute("code", HttpStatus.BAD_REQUEST);

        return "httpCodeView";
      }
    } else {
      // client mismatch
      logger.error(
          "readClientConfiguration failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute("code", HttpStatus.FORBIDDEN); // http 403

      return "httpCodeView";
    }
  }
Esempio n. 18
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);
  }
  /**
   * Update the metainformation for a given client.
   *
   * @param clientId
   * @param jsonString
   * @param m
   * @param auth
   * @return
   */
  @PreAuthorize(
      "hasRole('ROLE_CLIENT') and #oauth2.hasScope('"
          + SystemScopeService.RESOURCE_TOKEN_SCOPE
          + "')")
  @RequestMapping(
      value = "/{id}",
      method = RequestMethod.PUT,
      produces = MediaType.APPLICATION_JSON_VALUE,
      consumes = MediaType.APPLICATION_JSON_VALUE)
  public String updateProtectedResource(
      @PathVariable("id") String clientId,
      @RequestBody String jsonString,
      Model m,
      OAuth2Authentication auth) {

    ClientDetailsEntity newClient = null;
    try {
      newClient = ClientDetailsEntityJsonProcessor.parse(jsonString);
    } catch (JsonSyntaxException e) {
      // bad parse
      // didn't parse, this is a bad request
      logger.error("updateProtectedResource failed; submitted JSON is malformed");
      m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); // http 400
      return HttpCodeView.VIEWNAME;
    }

    ClientDetailsEntity oldClient = clientService.loadClientByClientId(clientId);

    if (newClient != null
        && oldClient != null // we have an existing client and the new one parsed
        && oldClient
            .getClientId()
            .equals(
                auth.getOAuth2Request()
                    .getClientId()) // the client passed in the URI matches the one in the auth
        && oldClient
            .getClientId()
            .equals(
                newClient.getClientId()) // the client passed in the body matches the one in the URI
    ) {

      // a client can't ask to update its own client secret to any particular value
      newClient.setClientSecret(oldClient.getClientSecret());

      newClient.setCreatedAt(oldClient.getCreatedAt());

      // no grant types are allowed
      newClient.setGrantTypes(new HashSet<String>());
      newClient.setResponseTypes(new HashSet<String>());
      newClient.setRedirectUris(new HashSet<String>());

      // don't issue tokens to this client
      newClient.setAccessTokenValiditySeconds(0);
      newClient.setIdTokenValiditySeconds(0);
      newClient.setRefreshTokenValiditySeconds(0);

      // clear out unused fields
      newClient.setDefaultACRvalues(new HashSet<String>());
      newClient.setDefaultMaxAge(null);
      newClient.setIdTokenEncryptedResponseAlg(null);
      newClient.setIdTokenEncryptedResponseEnc(null);
      newClient.setIdTokenSignedResponseAlg(null);
      newClient.setInitiateLoginUri(null);
      newClient.setPostLogoutRedirectUris(null);
      newClient.setRequestObjectSigningAlg(null);
      newClient.setRequireAuthTime(null);
      newClient.setReuseRefreshToken(false);
      newClient.setSectorIdentifierUri(null);
      newClient.setSubjectType(null);
      newClient.setUserInfoEncryptedResponseAlg(null);
      newClient.setUserInfoEncryptedResponseEnc(null);
      newClient.setUserInfoSignedResponseAlg(null);

      // this client has been dynamically registered (obviously)
      newClient.setDynamicallyRegistered(true);

      // this client has access to the introspection endpoint
      newClient.setAllowIntrospection(true);

      // do validation on the fields
      try {
        newClient = validateScopes(newClient);
        newClient = validateAuth(newClient);
      } catch (ValidationException ve) {
        // validation failed, return an error
        m.addAttribute(JsonErrorView.ERROR, ve.getError());
        m.addAttribute(JsonErrorView.ERROR_MESSAGE, ve.getErrorDescription());
        m.addAttribute(HttpCodeView.CODE, ve.getStatus());
        return JsonErrorView.VIEWNAME;
      }

      try {
        // save the client
        ClientDetailsEntity savedClient = clientService.updateClient(oldClient, newClient);

        // possibly update the token
        OAuth2AccessTokenEntity token = fetchValidRegistrationToken(auth, savedClient);

        RegisteredClient registered =
            new RegisteredClient(
                savedClient,
                token.getValue(),
                config.getIssuer()
                    + "resource/"
                    + UriUtils.encodePathSegment(savedClient.getClientId(), "UTF-8"));

        // send it all out to the view
        m.addAttribute("client", registered);
        m.addAttribute(HttpCodeView.CODE, HttpStatus.OK); // http 200

        return ClientInformationResponseView.VIEWNAME;
      } catch (UnsupportedEncodingException e) {
        logger.error("Unsupported encoding", e);
        m.addAttribute(HttpCodeView.CODE, HttpStatus.INTERNAL_SERVER_ERROR);
        return HttpCodeView.VIEWNAME;
      } catch (IllegalArgumentException e) {
        logger.error("Couldn't save client", e);

        m.addAttribute(JsonErrorView.ERROR, "invalid_client_metadata");
        m.addAttribute(
            JsonErrorView.ERROR_MESSAGE,
            "Unable to save client due to invalid or inconsistent metadata.");
        m.addAttribute(HttpCodeView.CODE, HttpStatus.BAD_REQUEST); // http 400

        return JsonErrorView.VIEWNAME;
      }
    } else {
      // client mismatch
      logger.error(
          "updateProtectedResource"
              + " failed, client ID mismatch: "
              + clientId
              + " and "
              + auth.getOAuth2Request().getClientId()
              + " do not match.");
      m.addAttribute(HttpCodeView.CODE, HttpStatus.FORBIDDEN); // http 403

      return HttpCodeView.VIEWNAME;
    }
  }