/*
   Dave had previously re-shared VIEW scope to ED, but that was made invalid when
   he lost that scope himself. He has been re-granted that scope so the VIEW scope
   should move the active policy and the empty inactive policy should be deleted.
  */
  @Test
  public void shouldUpdateInvalidRightsTree() throws Exception {
    // Given
    List<Resource> policies = excludePolicies(DAVE, ED);
    policies.add(makePolicy(DAVE, ED, true, DELETE));
    policies.add(makePolicy(DAVE, ED, false, VIEW));

    PolicyGraph graph = makePolicyGraph(policies);
    graph.computeGraph();

    given(delegate.updatePolicies(isNull(ServerContext.class), anySet()))
        .willReturn(
            Promises.<List<Resource>, ResourceException>newResultPromise(
                Collections.<Resource>emptyList()));

    given(delegate.deletePolicies(isNull(ServerContext.class), anySet()))
        .willReturn(
            Promises.<List<Resource>, ResourceException>newResultPromise(
                Collections.<Resource>emptyList()));

    // When
    Promise<List<List<Resource>>, ResourceException> promise = graph.update(null, delegate);

    // Then
    AssertJPromiseAssert.assertThat(promise).succeeded();
    assertThat(UmaPolicyUtils.getPolicyScopes(policyUpdated())).containsOnly(VIEW, DELETE);
    assertThat(policyDeleted()).isEqualTo("Dave-Ed-false");
    verifyNoMoreInteractions(delegate);
  }
  /*
   Alice has removed Dave's rights to EDIT, so EDIT needs removing from the
   active Dave -> Ed policy, and adding to an inactive policy.
  */
  @Test
  public void shouldRemoveLostRights() throws Exception {
    // Given
    List<Resource> policies = excludePolicies(DAVE, ED);
    policies.add(makePolicy(DAVE, ED, true, VIEW, DELETE, EDIT));

    PolicyGraph graph = makePolicyGraph(policies);
    graph.computeGraph();

    given(resourceSetStore.read(anyString()))
        .willReturn(new ResourceSetDescription(RESOURCE_SET_ID, "RESOURCE_SERVER_ID", ALICE, null));

    given(delegate.updatePolicies(isNull(ServerContext.class), anySet()))
        .willReturn(
            Promises.<List<Resource>, ResourceException>newResultPromise(
                Collections.<Resource>emptyList()));

    given(delegate.createPolicies(isNull(ServerContext.class), anySet()))
        .willReturn(
            Promises.<List<Resource>, ResourceException>newResultPromise(
                Collections.<Resource>emptyList()));

    // When
    Promise<List<List<Resource>>, ResourceException> promise = graph.update(null, delegate);

    // Then
    AssertJPromiseAssert.assertThat(promise).succeeded();
    JsonValue created = policyCreated();
    assertThat(UmaPolicyUtils.getPolicyScopes(created)).containsOnly(EDIT);
    assertThat(created.get("active").asBoolean()).isFalse();
    assertThat(UmaPolicyUtils.getPolicyScopes(policyUpdated())).containsOnly(VIEW, DELETE);
    verifyNoMoreInteractions(delegate);
  }
  /**
   * Creates the underlying backend policies.
   *
   * <p>NOTE: if the creation of the underlying policies fails, any successfully created underlying
   * policies will be attempted to be deleted but if the deletion fails, then the underlying
   * policies may be in an inconsistent state.
   *
   * @param context The request context.
   * @param policies The underlying policies to create.
   * @return A promise containing the list of created underlying policies or a {@code
   *     ResourceException} if the creation fails.
   */
  public Promise<List<Resource>, ResourceException> createPolicies(
      ServerContext context, Set<JsonValue> policies) {

    final List<String> policyIds = new ArrayList<String>();
    List<Promise<Resource, ResourceException>> promises =
        new ArrayList<Promise<Resource, ResourceException>>();
    for (JsonValue policy : policies) {
      promises.add(
          policyResource
              .handleCreate(context, Requests.newCreateRequest("", policy))
              .thenOnResult(
                  new ResultHandler<Resource>() {
                    @Override
                    public void handleResult(Resource result) {
                      // Save ids of created policies, in case a latter policy fails to be created,
                      // so we can roll back.
                      policyIds.add(result.getId());
                    }
                  }));
    }
    return Promises.when(promises)
        .thenAsync(
            new AsyncFunction<List<Resource>, List<Resource>, ResourceException>() {
              @Override
              public Promise<List<Resource>, ResourceException> apply(List<Resource> value) {
                return Promises.newResultPromise(value);
              }
            },
            new UmaPolicyCreateFailureHandler(context, policyIds));
  }
  @Test
  public void shouldReadResourceSet() throws Exception {

    // Given
    ServerContext context = mock(ServerContext.class);
    ReadRequest request = mock(ReadRequest.class);
    given(request.getFields()).willReturn(Arrays.asList(new JsonPointer("/fred")));
    ResultHandler<Resource> handler = mock(ResultHandler.class);
    ResourceSetDescription resourceSet = new ResourceSetDescription();
    resourceSet.setDescription(json(object()));
    Promise<ResourceSetDescription, ResourceException> resourceSetPromise =
        Promises.newSuccessfulPromise(resourceSet);

    given(contextHelper.getRealm(context)).willReturn("REALM");
    given(contextHelper.getUserId(context)).willReturn("RESOURCE_OWNER_ID");
    given(
            resourceSetService.getResourceSet(
                context, "REALM", "RESOURCE_SET_ID", "RESOURCE_OWNER_ID", false))
        .willReturn(resourceSetPromise);

    // When
    resource.readInstance(context, "RESOURCE_SET_ID", request, handler);

    // Then
    verify(handler).handleResult(Matchers.<Resource>anyObject());
  }
  @Test
  public void crestActionBlowupIsAllowed() throws SSOException, DelegationException {
    // Given...
    final Set<String> actions = new HashSet<>(Arrays.asList("MODIFY"));
    final DelegationPermission permission =
        new DelegationPermission(
            "/abc", "rest", "1.0", "policies", "destroy", actions, EXTENSIONS, DUMB_FUNC);
    given(factory.newInstance("/abc", "rest", "1.0", "policies", "destroy", actions, EXTENSIONS))
        .willReturn(permission);

    given(subjectContext.getCallerSSOToken()).willReturn(token);
    given(evaluator.isAllowed(eq(token), eq(permission), eq(ENVIRONMENT))).willReturn(true);

    JsonValue jsonValue = json(object(field("someKey", "someValue")));
    Promise<ActionResponse, ResourceException> promise =
        Promises.newResultPromise(Responses.newActionResponse(jsonValue));
    given(provider.actionCollection(isA(Context.class), isA(ActionRequest.class)))
        .willReturn(promise);

    // When...
    final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module);
    final Router router = new Router();
    router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain);

    final RealmContext context = new RealmContext(subjectContext);
    context.setSubRealm("abc", "abc");
    final ActionRequest request = Requests.newActionRequest("/policies", "blowup");
    Promise<ActionResponse, ResourceException> result = router.handleAction(context, request);

    // Then...
    assertThat(result).succeeded().withContent().stringAt("someKey").isEqualTo("someValue");
  }
 // 1st time called: Mock a 401 (Unauthorized status) response
 @Override
 public Promise<Response, NeverThrowsException> answer(InvocationOnMock invocation)
     throws Throwable {
   Response response = new Response();
   response.setStatus(Status.UNAUTHORIZED);
   response.getHeaders().putSingle(AUTHENTICATE_HEADER, "Basic realm=\"Login\"");
   return Promises.newResultPromise(response);
 }
 /**
  * Deletes the underlying backend policies.
  *
  * <p>NOTE: if the deletion of the underlying policies fails, the underlying policies may be in an
  * inconsistent state.
  *
  * @param context The request context.
  * @param policyIds The list of ids of the underlying backend policies to delete.
  * @return A promise containing the list of underlying policies that were deleted or a {@code
  *     ResourceException} if the policies were failed to be deleted.
  */
 public Promise<List<Resource>, ResourceException> deletePolicies(
     ServerContext context, Collection<String> policyIds) {
   List<Promise<Resource, ResourceException>> promises =
       new ArrayList<Promise<Resource, ResourceException>>();
   for (String policyId : policyIds) {
     promises.add(policyResource.handleDelete(context, Requests.newDeleteRequest(policyId)));
   }
   return Promises.when(promises);
 }
  /**
   * Calls each {@code AsyncServerAuthContext} in parallel to clean the client subject and only
   * return a successful promise if all complete successfully otherwise returns the first exception
   * in a failed promise.
   *
   * @param context {@inheritDoc}
   * @param clientSubject {@inheritDoc}
   * @return {@inheritDoc}
   */
  @Override
  public Promise<Void, AuthenticationException> cleanSubject(
      MessageContext context, Subject clientSubject) {

    List<Promise<Void, AuthenticationException>> promises = new ArrayList<>();
    for (AsyncServerAuthModule serverAuthModule : authModules) {
      promises.add(serverAuthModule.cleanSubject(context, clientSubject));
    }
    return Promises.when(promises).thenAsync(ON_SUCCESS_RETURN_VOID);
  }
 @Override
 public Promise<Response, NeverThrowsException> handle(Context context, Request request) {
   try {
     latch2.countDown();
     latch1.await();
     return null;
   } catch (InterruptedException e) {
     return Promises.newResultPromise(new Response(Status.INTERNAL_SERVER_ERROR));
   }
 }
 /**
  * Updates the underlying backend policies.
  *
  * <p>NOTE: if the update of the underlying policies fails, the underlying policies may be in an
  * inconsistent state.
  *
  * @param context The request context.
  * @param policies The updated underlying policies to update.
  * @return A promise containing the list of updated underlying policies or a {@code
  *     ResourceException} if the update failed.
  */
 public Promise<List<Resource>, ResourceException> updatePolicies(
     ServerContext context, Set<JsonValue> policies) {
   List<Promise<Resource, ResourceException>> promises =
       new ArrayList<Promise<Resource, ResourceException>>();
   for (JsonValue policy : policies) {
     String policyName = policy.get("name").asString();
     promises.add(
         policyResource.handleUpdate(context, Requests.newUpdateRequest(policyName, policy)));
   }
   return Promises.when(promises);
 }
    @Override
    public Promise<Response, NeverThrowsException> answer(InvocationOnMock invocation)
        throws Throwable {
      Request request = (Request) invocation.getArguments()[1];

      // Verify the authorization header: base64(user:pass)
      assertThat(request.getHeaders().getFirst(AUTHORIZATION_HEADER))
          .isEqualTo("Basic " + credentials);

      // Produce a valid response, no special headers are required
      Response response = new Response();
      response.setStatus(Status.OK);
      return Promises.newResultPromise(response);
    }
 /** {@inheritDoc} */
 @Override
 public Promise<AuthorizationResult, ResourceException> authorizeCreate(
     Context context, CreateRequest request) {
   try {
     if (!getUserId(context).equalsIgnoreCase(getUserIdFromUri(context))) {
       return Promises.newResultPromise(
           AuthorizationResult.accessDenied(
               "Only resource owner of resource set can create UMA " + "policies for it."));
     } else {
       return authorize(context);
     }
   } catch (ResourceException e) {
     return e.asPromise();
   }
 }
  @Test
  public void nameQueryShouldBeSupported() throws Exception {

    // Given
    ServerContext context = mock(ServerContext.class);
    QueryRequest request = mock(QueryRequest.class);
    given(request.getFields()).willReturn(Arrays.asList(new JsonPointer("/fred")));
    QueryResultHandler handler = mock(QueryResultHandler.class);
    ResourceSetDescription resourceSet = mock(ResourceSetDescription.class);
    QueryFilter queryFilter =
        QueryFilter.and(
            QueryFilter.equalTo("/name", "NAME"),
            QueryFilter.equalTo("/resourceServer", "myclient"),
            QueryFilter.equalTo("/policy/permissions/subject", "SUBJECT"));
    Promise<Collection<ResourceSetDescription>, ResourceException> resourceSetsPromise =
        Promises.newSuccessfulPromise((Collection<ResourceSetDescription>) asSet(resourceSet));

    given(contextHelper.getRealm(context)).willReturn("REALM");
    given(contextHelper.getUserId(context)).willReturn("RESOURCE_OWNER_ID");
    given(request.getQueryFilter()).willReturn(queryFilter);
    given(
            resourceSetService.getResourceSets(
                eq(context),
                eq("REALM"),
                Matchers.<ResourceSetWithPolicyQuery>anyObject(),
                eq("RESOURCE_OWNER_ID"),
                eq(false)))
        .willReturn(resourceSetsPromise);

    // When
    resource.queryCollection(context, request, handler);

    // Then
    ArgumentCaptor<ResourceSetWithPolicyQuery> queryCaptor =
        ArgumentCaptor.forClass(ResourceSetWithPolicyQuery.class);
    verify(resourceSetService)
        .getResourceSets(
            eq(context), eq("REALM"), queryCaptor.capture(), eq("RESOURCE_OWNER_ID"), eq(false));
    assertThat(queryCaptor.getValue().getOperator()).isEqualTo(AggregateQuery.Operator.AND);
    assertThat(queryCaptor.getValue().getPolicyQuery())
        .isEqualTo(QueryFilter.equalTo("/permissions/subject", "SUBJECT"));
    assertThat(queryCaptor.getValue().getResourceSetQuery())
        .isEqualTo(
            org.forgerock.util.query.QueryFilter.and(
                org.forgerock.util.query.QueryFilter.equalTo("name", "NAME"),
                org.forgerock.util.query.QueryFilter.equalTo("clientId", "myclient")));
    verify(handler).handleResult(any(QueryResult.class));
  }
  /**
   * Secures the response message using the same {@code AsyncServerAuthModule} that authenticated
   * the incoming request message.
   *
   * <p>If no {@code AsyncServerAuthModule} authenticated the incoming request message, then this
   * method should not have been called and a failed promise will be return with an {@code
   * AuthenticationException}.
   *
   * @param context {@inheritDoc}
   * @param serviceSubject {@inheritDoc}
   * @return {@inheritDoc}
   */
  @Override
  public Promise<AuthStatus, AuthenticationException> secureResponse(
      MessageContext context, Subject serviceSubject) {
    FallbackAuthContextState state = context.getState(this);

    if (state.getAuthenticatedAuthModuleIndex() < 0) {
      return Promises.newExceptionPromise(
          new AuthenticationException(
              "No auth module authenticated the incoming request message. "
                  + "Cannot secure response message."));
    }
    AsyncServerAuthModule authModule = authModules.get(state.getAuthenticatedAuthModuleIndex());
    logger.debug(
        "Using authenticating auth module from private context map, {}, to secure the response",
        authModule.getModuleId());

    return authModule.secureResponse(context, serviceSubject);
  }
  @Test
  public void revokeAllUserPoliciesActionShouldHandleResourceException() {

    // Given
    ServerContext context = mock(ServerContext.class);
    ActionRequest request = mock(ActionRequest.class);
    ResultHandler<JsonValue> handler = mock(ResultHandler.class);

    given(contextHelper.getRealm(context)).willReturn("REALM");
    given(contextHelper.getUserId(context)).willReturn("RESOURCE_OWNER_ID");
    given(request.getAction()).willReturn("revokeAll");
    given(resourceSetService.revokeAllPolicies(context, "REALM", "RESOURCE_OWNER_ID"))
        .willReturn(Promises.<Void, ResourceException>newFailedPromise(new NotFoundException()));

    // When
    resource.actionCollection(context, request, handler);

    // Then
    verify(handler).handleError(Matchers.<ResourceException>anyObject());
    verify(handler, never()).handleResult(Matchers.<JsonValue>anyObject());
  }
 private Promise<AuthStatus, AuthenticationException> validateRequest(
     final MessageInfoContext messageInfo,
     final Subject clientSubject,
     final Subject serviceSubject) {
   if (position < authModules.size()) {
     final AsyncServerAuthModule authModule = authModules.get(position);
     return authModule
         .validateRequest(messageInfo, clientSubject, serviceSubject)
         .thenOnResult(
             new ResultHandler<AuthStatus>() {
               @Override
               public void handleResult(AuthStatus authStatus) {
                 if (isSuccess(authStatus)) {
                   /*
                    * Save the index of the authenticating module so that it can
                    * be retrieved when securing the response
                    */
                   logger.trace(
                       "Adding authenticating auth module to private context map, {}",
                       authModule.getClass().getSimpleName());
                   state.setAuthenticatedAuthModuleIndex(position);
                 }
               }
             })
         .thenAsync(
             new AsyncFunction<AuthStatus, AuthStatus, AuthenticationException>() {
               @Override
               public Promise<AuthStatus, AuthenticationException> apply(AuthStatus authStatus) {
                 if (isSendFailure(authStatus)) {
                   return next().validateRequest(messageInfo, clientSubject, serviceSubject);
                 } else {
                   return Promises.newResultPromise(authStatus);
                 }
               }
             });
   } else {
     return Promises.newResultPromise(AuthStatus.SEND_FAILURE);
   }
 }
  /*
   Dave had removed Ed's ability to DELETE, so Ed's resharing policy to Bob had been
   made inactive. Dave has re-granted Ed's DELETE, so the inactive policy can be made
   active again.
  */
  @Test
  public void shouldSwitchAllScopesInvalid() throws Exception {
    // Given
    List<Resource> policies = excludePolicies(ED, BOB);
    policies.add(makePolicy(ED, BOB, false, DELETE));

    PolicyGraph graph = makePolicyGraph(policies);
    graph.computeGraph();

    given(delegate.updatePolicies(isNull(ServerContext.class), anySet()))
        .willReturn(
            Promises.<List<Resource>, ResourceException>newResultPromise(
                Collections.<Resource>emptyList()));

    // When
    Promise<List<List<Resource>>, ResourceException> promise = graph.update(null, delegate);

    // Then
    AssertJPromiseAssert.assertThat(promise).succeeded();
    assertThat(policyUpdated().get("active").asBoolean()).isTrue();
    verifyNoMoreInteractions(delegate);
  }
 /** {@inheritDoc} */
 @Override
 public Promise<List<Resource>, ResourceException> apply(final ResourceException error) {
   List<Promise<Resource, ResourceException>> promises =
       new ArrayList<Promise<Resource, ResourceException>>();
   PromiseImpl<Resource, ResourceException> kicker = PromiseImpl.create();
   promises.add(kicker);
   for (String id : policyIds) {
     promises.add(policyResource.handleDelete(context, Requests.newDeleteRequest(id)));
   }
   Promise<List<Resource>, ResourceException> promise =
       Promises.when(promises)
           .thenAsync(
               new AsyncFunction<List<Resource>, List<Resource>, ResourceException>() {
                 @Override
                 public Promise<List<Resource>, ResourceException> apply(List<Resource> value) {
                   // If we succeed in deleting then return the original error
                   return Promises.newExceptionPromise(error);
                 }
               });
   kicker.handleResult(null);
   return promise;
 }
  /**
   * Allows users to query OAuth2 applications that they have given their consent access to and that
   * have active access and/or refresh tokens.
   *
   * <p>Applications consist of an id, a name (the client id), a set of scopes and an expiry time.
   * The scopes field is the union of the scopes of the individual access/refresh tokens. The expiry
   * time is the time when the last access/refresh token will expire, or null if the server is
   * configured to allow tokens to be refreshed indefinitely.
   *
   * @param context The request context.
   * @param queryHandler The query handler.
   * @param request Unused but necessary for used of the {@link @Query} annotation.
   * @return A promise of a query response.
   */
  @Query
  public Promise<QueryResponse, ResourceException> query(
      Context context, QueryResourceHandler queryHandler, QueryRequest request) {
    String userId = contextHelper.getUserId(context);
    String realm = contextHelper.getRealm(context);

    try {
      QueryFilter<CoreTokenField> queryFilter = getQueryFilter(userId, realm);

      JsonValue tokens = tokenStore.query(queryFilter);

      Map<String, Set<JsonValue>> applicationTokensMap = new HashMap<>();

      for (JsonValue token : tokens) {
        String clientId = getAttributeValue(token, CLIENT_ID.getOAuthField());
        Set<JsonValue> applicationTokens = applicationTokensMap.get(clientId);
        if (applicationTokens == null) {
          applicationTokens = new HashSet<>();
          applicationTokensMap.put(clientId, applicationTokens);
        }
        applicationTokens.add(token);
      }

      for (Map.Entry<String, Set<JsonValue>> applicationTokens : applicationTokensMap.entrySet()) {
        ResourceResponse resource =
            getResourceResponse(context, applicationTokens.getKey(), applicationTokens.getValue());
        queryHandler.handleResource(resource);
      }

      return Promises.newResultPromise(Responses.newQueryResponse());
    } catch (CoreTokenException | ServerException | InvalidClientException | NotFoundException e) {
      debug.message("Failed to query OAuth2 clients for user {}", userId, e);
      return new InternalServerErrorException(e).asPromise();
    } catch (InternalServerErrorException e) {
      debug.message("Failed to query OAuth2 clients for user {}", userId, e);
      return e.asPromise();
    }
  }
  @Test
  public void shouldRevokeAllUserPolicies() {

    // Given
    ServerContext context = mock(ServerContext.class);
    ActionRequest request = mock(ActionRequest.class);
    ResultHandler<JsonValue> handler = mock(ResultHandler.class);

    given(contextHelper.getRealm(context)).willReturn("REALM");
    given(contextHelper.getUserId(context)).willReturn("RESOURCE_OWNER_ID");
    given(request.getAction()).willReturn("revokeAll");
    given(resourceSetService.revokeAllPolicies(context, "REALM", "RESOURCE_OWNER_ID"))
        .willReturn(Promises.<Void, ResourceException>newSuccessfulPromise(null));

    // When
    resource.actionCollection(context, request, handler);

    // Then
    ArgumentCaptor<JsonValue> jsonCaptor = ArgumentCaptor.forClass(JsonValue.class);
    verify(handler).handleResult(jsonCaptor.capture());
    verify(handler, never()).handleError(Matchers.<ResourceException>anyObject());
    assertThat(jsonCaptor.getValue().asMap()).isEmpty();
  }
  @Test
  public void crestQueryIsAllowed() throws SSOException, DelegationException, ResourceException {
    // Given...
    final Set<String> actions = new HashSet<>(Arrays.asList("READ"));
    final DelegationPermission permission =
        new DelegationPermission(
            "/abc", "rest", "1.0", "policies", "read", actions, EXTENSIONS, DUMB_FUNC);
    given(factory.newInstance("/abc", "rest", "1.0", "policies", "read", actions, EXTENSIONS))
        .willReturn(permission);

    given(subjectContext.getCallerSSOToken()).willReturn(token);
    given(evaluator.isAllowed(eq(token), eq(permission), eq(ENVIRONMENT))).willReturn(true);

    QueryResourceHandler handler = mock(QueryResourceHandler.class);
    Promise<QueryResponse, ResourceException> promise =
        Promises.newResultPromise(Responses.newQueryResponse("abc-def"));
    given(
            provider.queryCollection(
                isA(Context.class), isA(QueryRequest.class), isA(QueryResourceHandler.class)))
        .willReturn(promise);

    // When...
    final FilterChain chain = AuthorizationFilters.createAuthorizationFilter(provider, module);
    final Router router = new Router();
    router.addRoute(RoutingMode.STARTS_WITH, Router.uriTemplate("/policies"), chain);

    final RealmContext context = new RealmContext(subjectContext);
    context.setSubRealm("abc", "abc");
    final QueryRequest request = Requests.newQueryRequest("/policies");
    Promise<QueryResponse, ResourceException> result =
        router.handleQuery(context, request, handler);

    // Then...
    QueryResponse response = result.getOrThrowUninterruptibly();
    assertThat(response.getPagedResultsCookie()).isEqualTo("abc-def");
  }
 @Override
 public Promise<Response, NeverThrowsException> handle(
     final Context context, final Request request) {
   return Promises.newResultPromise(response);
 }