/**
   * Queries relationships, returning the relationship associated with this providers resource path
   * and the specified relationship field.
   *
   * @param context The current context
   * @param managedObjectId The id of the managed object to find relationships associated with
   * @return
   */
  private Promise<ResourceResponse, ResourceException> queryRelationship(
      final Context context, final String managedObjectId) {
    try {
      final QueryRequest queryRequest = Requests.newQueryRequest(REPO_RESOURCE_PATH);
      final List<ResourceResponse> relationships = new ArrayList<>();

      queryRequest.setQueryFilter(
          QueryFilter.and(
              QueryFilter.equalTo(
                  new JsonPointer(isReverse ? REPO_FIELD_SECOND_ID : REPO_FIELD_FIRST_ID),
                  resourcePath.child(managedObjectId)),
              QueryFilter.equalTo(new JsonPointer(REPO_FIELD_FIRST_PROPERTY_NAME), propertyName)));

      connectionFactory.getConnection().query(context, queryRequest, relationships);

      if (relationships.isEmpty()) {
        return new NotFoundException().asPromise();
      } else {
        // TODO OPENIDM-4094 - check size and throw illegal state if more than one?
        return newResultPromise(FORMAT_RESPONSE.apply(relationships.get(0)));
      }
    } catch (ResourceException e) {
      return e.asPromise();
    }
  }
  private boolean authenticate(Credential credential, SecurityContextMapper securityContextMapper)
      throws AuthException {

    if (!credential.isComplete()) {
      logger.debug("Failed authentication, missing or empty headers");
      return false;
    }

    // set the authenticationId of the user that is trying to authenticate
    securityContextMapper.setAuthenticationId(credential.username);

    try {
      return authenticator.authenticate(
          credential.username,
          credential.password,
          authnFilterHelper.getRouter().createServerContext());
    } catch (ResourceException e) {
      logger.debug(
          "Failed delegated authentication of {} on {}.", credential.username, queryOnResource, e);
      if (e.isServerError()) { // HTTP server-side error
        throw new JaspiAuthException(
            "Failed delegated authentication of " + credential.username + " on " + queryOnResource,
            e);
      }
      // authentication failed
      return false;
    }
  }
  protected ResourceException convertScriptException(final ScriptException scriptException) {

    ResourceException convertedError;
    try {
      throw scriptException;
    } catch (ScriptThrownException e) {
      convertedError =
          e.toResourceException(ResourceException.INTERNAL_ERROR, scriptException.getMessage());
    } catch (ScriptException e) {
      convertedError =
          new InternalServerErrorException(scriptException.getMessage(), scriptException);
    }
    if (convertedError.getDetail().isNull()) {
      convertedError.setDetail(new JsonValue(new HashMap<String, Object>()));
    }
    final JsonValue detail = convertedError.getDetail();
    if (detail.get("fileName").isNull()
        && detail.get("lineNumber").isNull()
        && detail.get("columnNumber").isNull()) {
      detail.put("fileName", scriptException.getFileName());
      detail.put("lineNumber", scriptException.getLineNumber());
      detail.put("columnNumber", scriptException.getColumnNumber());
    }

    return convertedError;
  }
  /** {@inheritDoc} */
  public Promise<ResourceResponse, ResourceException> readInstance(
      Context context, String resourceId, ReadRequest request) {

    final String realm = getRealm(context);

    debug.message("ServerInfoResource :: READ : in realm: " + realm);

    if (COOKIE_DOMAINS.equalsIgnoreCase(resourceId)) {
      return getCookieDomains();
    } else if (ALL_SERVER_INFO.equalsIgnoreCase(resourceId)) {
      return getAllServerInfo(context, realm);
    } else { // for now this is the only case coming in, so fail if otherwise
      final ResourceException e =
          new NotSupportedException("ResourceId not supported: " + resourceId);
      if (debug.errorEnabled()) {
        debug.error(
            "ServerInfoResource :: READ : in realm: "
                + realm
                + ": Cannot receive information on requested resource: "
                + resourceId,
            e);
      }
      return e.asPromise();
    }
  }
  /**
   * Returns the <code>Dictionary</code> for the given <code>pid</code>.
   *
   * @param pid The identifier for the dictionary to load.
   * @return The dictionary for the identifier. This must not be <code>null</code> but may be empty.
   * @throws IOException If an error occurrs loading the dictionary. An <code>IOException</code>
   *     must also be thrown if no dictionary exists for the given identifier.
   */
  public Dictionary load(String pid) throws IOException {
    logger.debug("Config load call for {}", pid);
    Dictionary result = null;

    try {
      if (isReady(0) && requireRepository) {
        String id = pidToId(pid);
        ReadRequest readRequest = Requests.newReadRequest(id);
        Resource existing = repo.read(readRequest);
        Map<String, Object> existingConfig = existing.getContent().asMap();
        Object configMap = existingConfig.get(JSONEnhancedConfig.JSON_CONFIG_PROPERTY);
        String configString = serializeConfig(configMap);
        existingConfig.put(JSONEnhancedConfig.JSON_CONFIG_PROPERTY, configString);
        logger.debug("Config loaded {} {}", pid, existing);
        result = mapToDict(existingConfig);
      } else if (!requireRepository) {
        result = tempStore.get(pid);
        if (result == null) {
          throw new IOException("No entry for " + pid + " exists.");
        }

        logger.debug("Config loaded from temporary store {} {}", pid, result);
      }
    } catch (NotFoundException ex) {
      result = tempStore.get(pid);
      if (result == null) {
        throw new IOException("No entry for " + pid + " exists.");
      }

      logger.debug("Config loaded from temporary store {} {}", pid, result);
    } catch (ResourceException ex) {
      throw new IOException("Failed to load configuration in repository: " + ex.getMessage(), ex);
    }
    return result;
  }
 public Promise<ResourceResponse, ResourceException> handleUpdate(
     Context context, UpdateRequest request) {
   EventEntry measure =
       Publisher.start(
           Name.get(
               "openidm/internal/script/" + this.getScriptEntry().getName().getName() + "/update"),
           null,
           null);
   try {
     final ScriptEntry _scriptEntry = getScriptEntry();
     if (!_scriptEntry.isActive()) {
       throw new ServiceUnavailableException("Inactive script: " + _scriptEntry.getName());
     }
     final Script script = _scriptEntry.getScript(context);
     script.setBindings(script.createBindings());
     customizer.handleUpdate(context, request, script.getBindings());
     return evaluate(request, script);
   } catch (ScriptException e) {
     return convertScriptException(e).asPromise();
   } catch (ResourceException e) {
     return e.asPromise();
   } catch (Exception e) {
     return new InternalServerErrorException(e.getMessage(), e).asPromise();
   } finally {
     measure.end();
   }
 }
  /**
   * Returns <code>true</code> if a persisted <code>Dictionary</code> exists for the given <code>pid
   * </code>.
   *
   * @param pid The identifier for the dictionary to test.
   */
  public boolean exists(String pid) {
    logger.debug("Config exists call for {}", pid);

    boolean exists = false;

    if (isReady(0) && requireRepository) {
      String id = pidToId(pid);
      try {
        ReadRequest readRequest = Requests.newReadRequest(id);
        Resource existing = repo.read(readRequest);
        exists = (existing != null);
      } catch (NotFoundException ex) {
        exists = false;
      } catch (ResourceException ex) {
        throw new RuntimeException(
            "Failed to check if configuration exists in repository: " + ex.getMessage(), ex);
      }
    }
    if (!exists) {
      exists = tempStore.containsKey(pid);
      if (exists) {
        logger.debug("Entry exists in temporary store for '{}'", pid);
      }
    } else {
      logger.debug("Entry exists for '{}'", pid);
    }

    if (!exists) {
      logger.debug("Entry does not exist for '{}'", pid);
    }
    return exists;
  }
  @Override
  public Promise<ResourceResponse, ResourceException> createInstance(
      Context context, CreateRequest request) {
    TokenGenerationServiceInvocationState invocationState;
    try {
      invocationState = TokenGenerationServiceInvocationState.fromJson(request.getContent());
    } catch (Exception e) {
      logger.error(
          "Exception caught marshalling json into TokenGenerationServiceInvocationState instance: "
              + e);
      return new BadRequestException(e.getMessage(), e).asPromise();
    }
    SSOToken subjectToken;
    try {
      subjectToken = validateAssertionSubjectSession(invocationState);
    } catch (ForbiddenException e) {
      return e.asPromise();
    }

    STSInstanceState stsInstanceState;
    try {
      stsInstanceState = getSTSInstanceState(invocationState);
    } catch (ResourceException e) {
      return e.asPromise();
    }

    if (TokenType.SAML2.equals(invocationState.getTokenType())) {
      try {
        final String assertion =
            saml2TokenGeneration.generate(subjectToken, stsInstanceState, invocationState);
        return newResultPromise(issuedTokenResource(assertion));
      } catch (TokenCreationException e) {
        logger.error("Exception caught generating saml2 token: " + e, e);
        return e.asPromise();
      } catch (Exception e) {
        logger.error("Exception caught generating saml2 token: " + e, e);
        return new InternalServerErrorException(e.toString(), e).asPromise();
      }
    } else if (TokenType.OPENIDCONNECT.equals(invocationState.getTokenType())) {
      try {
        final String assertion =
            openIdConnectTokenGeneration.generate(subjectToken, stsInstanceState, invocationState);
        return newResultPromise(issuedTokenResource(assertion));
      } catch (TokenCreationException e) {
        logger.error("Exception caught generating OpenIdConnect token: " + e, e);
        return e.asPromise();
      } catch (Exception e) {
        logger.error("Exception caught generating OpenIdConnect token: " + e, e);
        return new InternalServerErrorException(e.toString(), e).asPromise();
      }
    } else {
      String message = "Bad request: unexpected token type:" + invocationState.getTokenType();
      logger.error(message);
      return new BadRequestException(message).asPromise();
    }
  }
Beispiel #9
0
  /**
   * Returns names of all realms included in the subtree rooted by the realm indicated in the query
   * url.
   *
   * <p>Names are unsorted and given as full paths.
   *
   * <p>Filtering, sorting, and paging of results is not supported.
   *
   * <p>{@inheritDoc}
   */
  @Override
  public void queryCollection(
      final ServerContext context, final QueryRequest request, final QueryResultHandler handler) {

    final String principalName = PrincipalRestUtils.getPrincipalNameFromServerContext(context);
    final RealmContext realmContext = context.asContext(RealmContext.class);
    final String realmPath = realmContext.getResolvedRealm();

    try {

      final SSOTokenManager mgr = SSOTokenManager.getInstance();
      final SSOToken ssoToken = mgr.createSSOToken(getCookieFromServerContext(context));

      final OrganizationConfigManager ocm = new OrganizationConfigManager(ssoToken, realmPath);
      final List<String> realmsInSubTree = new ArrayList<String>();
      realmsInSubTree.add(realmPath);
      for (final Object subRealmRelativePath : ocm.getSubOrganizationNames("*", true)) {
        if (realmPath.endsWith("/")) {
          realmsInSubTree.add(realmPath + subRealmRelativePath);
        } else {
          realmsInSubTree.add(realmPath + "/" + subRealmRelativePath);
        }
      }

      debug.message("RealmResource :: QUERY : performed by " + principalName);

      for (final Object realmName : realmsInSubTree) {
        JsonValue val = new JsonValue(realmName);
        Resource resource = new Resource((String) realmName, "0", val);
        handler.handleResource(resource);
      }
      handler.handleResult(new QueryResult());

    } catch (SSOException ex) {
      debug.error("RealmResource :: QUERY by " + principalName + " failed : " + ex);
      handler.handleError(ResourceException.getException(ResourceException.FORBIDDEN));

    } catch (SMSException ex) {
      debug.error("RealmResource :: QUERY by " + principalName + " failed :" + ex);
      switch (ex.getExceptionCode()) {
        case STATUS_NO_PERMISSION:
          // This exception will be thrown if permission to read realms from SMS has not been
          // delegated
          handler.handleError(ResourceException.getException(ResourceException.FORBIDDEN));
          break;
        default:
          handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR));
          break;
      }
    }
  }
 protected static void assertQueryPromiseFailedWithCodes(
     Promise<QueryResponse, ResourceException> promise,
     int resourceErrorCode,
     int entitlementErrorCode) {
   try {
     promise.getOrThrowUninterruptibly();
     fail("Should throw ResourceException");
   } catch (ResourceException e) {
     Assertions.assertThat(e.getCode()).isEqualTo(resourceErrorCode);
     Assertions.assertThat(e.getCause()).isInstanceOf(EntitlementException.class);
     Assertions.assertThat(((EntitlementException) e.getCause()).getErrorCode())
         .isEqualTo(entitlementErrorCode);
   }
 }
 /** {@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();
   }
 }
  /**
   * Persist the supplied {@link JsonValue} {@code value} as the new state of this singleton
   * relationship on {@code resourceId}.
   *
   * <p><em>This is currently the only means of creating an instance of this singleton</em>
   *
   * @param context The context of this request
   * @param resourceId Id of the resource relation fields in value are to be memebers of
   * @param value A {@link JsonValue} map of relationship fields and their values
   * @return The persisted instance of {@code value}
   */
  @Override
  public Promise<JsonValue, ResourceException> setRelationshipValueForResource(
      Context context, String resourceId, JsonValue value) {
    if (value.isNotNull()) {
      try {
        final JsonValue id = value.get(FIELD_ID);

        // Update if we got an id, otherwise replace
        if (id != null && id.isNotNull()) {
          final UpdateRequest updateRequest = Requests.newUpdateRequest("", value);
          updateRequest.setAdditionalParameter(PARAM_FIRST_ID, resourceId);
          return updateInstance(context, value.get(FIELD_ID).asString(), updateRequest)
              .then(
                  new Function<ResourceResponse, JsonValue, ResourceException>() {
                    @Override
                    public JsonValue apply(ResourceResponse resourceResponse)
                        throws ResourceException {
                      return resourceResponse.getContent();
                    }
                  });
        } else { // no id, replace current instance
          clear(context, resourceId);

          final CreateRequest createRequest = Requests.newCreateRequest("", value);
          createRequest.setAdditionalParameter(PARAM_FIRST_ID, resourceId);
          return createInstance(context, createRequest)
              .then(
                  new Function<ResourceResponse, JsonValue, ResourceException>() {
                    @Override
                    public JsonValue apply(ResourceResponse resourceResponse)
                        throws ResourceException {
                      return resourceResponse.getContent();
                    }
                  });
        }
      } catch (ResourceException e) {
        return e.asPromise();
      }

    } else {
      clear(context, resourceId);
      return newResultPromise(json(null));
    }
  }
  /**
   * Calls buildAuditEvent() and invokes the request to the audit path.
   *
   * @param connectionFactory factory used to make crest call to audit service.
   */
  public final Promise<ResourceResponse, ResourceException> log(
      ConfigAuditState configAuditState,
      Request request,
      Context context,
      ConnectionFactory connectionFactory) {
    try {

      JsonValue before = configAuditState.getBefore();
      JsonValue after = configAuditState.getAfter();

      // Get authenticationId from security context, if it exists.
      String authenticationId =
          (context.containsContext(SecurityContext.class))
              ? context.asContext(SecurityContext.class).getAuthenticationId()
              : null;

      // Build the event utilizing the config builder.
      AuditEvent auditEvent =
          ConfigAuditEventBuilder.configEvent()
              .resourceOperationFromRequest(request)
              .authenticationFromSecurityContext(context)
              .runAs(authenticationId)
              .transactionIdFromRootContext(context)
              .revision(configAuditState.getRevision())
              .timestamp(System.currentTimeMillis())
              .eventName(CONFIG_AUDIT_EVENT_NAME)
              .before(null != before ? before.toString() : "")
              .after(null != after ? after.toString() : "")
              .changedFields(getChangedFields(before, after))
              .toEvent();

      return connectionFactory
          .getConnection()
          .create(context, Requests.newCreateRequest(AUDIT_CONFIG_REST_PATH, auditEvent.getValue()))
          .asPromise();
    } catch (ResourceException e) {
      LOGGER.error("had trouble logging audit event for config changes.", e);
      return e.asPromise();
    } catch (Exception e) {
      LOGGER.error("had trouble logging audit event for config changes.", e);
      return new InternalServerErrorException(e.getMessage(), e).asPromise();
    }
  }
Beispiel #14
0
  /**
   * Records an 'access' audit event before and after the filtered CREST resource receives an action
   * request.
   *
   * <p>If the 'before' audit event fails due to an error, the request is cancelled and an error
   * response is returned. If the 'after' audit event fails due to an error, the request is not
   * cancelled as it's affects may have already been applied.
   *
   * @param context {@inheritDoc}
   * @param request {@inheritDoc}
   * @param next {@inheritDoc}
   */
  @Override
  public Promise<ActionResponse, ResourceException> filterAction(
      Context context, ActionRequest request, RequestHandler next) {

    final AuditingResultHandler auditingHandler = newAuditingResultHandler(context, request);
    try {
      auditingHandler.auditAccessAttempt();
    } catch (AuditException e) {
      return newExceptionPromise(ResourceException.getException(ResourceException.INTERNAL_ERROR));
    }

    return auditResponse(next.handleAction(context, request), auditingHandler);
  }
 public Promise<ActionResponse, ResourceException> handleAction(
     final Context context, final ActionRequest request) {
   EventEntry measure =
       Publisher.start(
           Name.get(
               "openidm/internal/script/" + this.getScriptEntry().getName().getName() + "/action"),
           null,
           null);
   try {
     final ScriptEntry _scriptEntry = getScriptEntry();
     if (!_scriptEntry.isActive()) {
       throw new ServiceUnavailableException("Inactive script: " + _scriptEntry.getName());
     }
     final Script script = _scriptEntry.getScript(context);
     script.setBindings(script.createBindings());
     customizer.handleAction(context, request, script.getBindings());
     Object result = script.eval();
     if (null == result) {
       return newActionResponse(new JsonValue(null)).asPromise();
     } else if (result instanceof JsonValue) {
       return newActionResponse((JsonValue) result).asPromise();
     } else if (result instanceof Map) {
       return newActionResponse(new JsonValue(result)).asPromise();
     } else {
       JsonValue resource = new JsonValue(new HashMap<String, Object>(1));
       resource.put("result", result);
       return newActionResponse(new JsonValue(result)).asPromise();
     }
   } catch (ScriptException e) {
     return convertScriptException(e).asPromise();
   } catch (ResourceException e) {
     return e.asPromise();
   } catch (Exception e) {
     return new InternalServerErrorException(e.getMessage(), e).asPromise();
   } finally {
     measure.end();
   }
 }
 @Override
 public Promise<ActionResponse, ResourceException> handleAction(
     final Context context, final ActionRequest request) {
   try {
     Map<String, String> params = request.getAdditionalParameters();
     switch (request.getActionAsEnum(Action.class)) {
       case updateDbCredentials:
         String newUser = params.get("user");
         String newPassword = params.get("password");
         if (newUser == null || newPassword == null) {
           return adapt(new BadRequestException("Expecting 'user' and 'password' parameters"))
               .asPromise();
         }
         synchronized (dbLock) {
           DBHelper.updateDbCredentials(dbURL, user, password, newUser, newPassword);
           JsonValue config =
               connectionFactory
                   .getConnection()
                   .read(context, Requests.newReadRequest("config", PID))
                   .getContent();
           config.put("user", newUser);
           config.put("password", newPassword);
           UpdateRequest updateRequest = Requests.newUpdateRequest("config/" + PID, config);
           connectionFactory.getConnection().update(context, updateRequest);
           return newActionResponse(new JsonValue(params)).asPromise();
         }
       case command:
         return newActionResponse(new JsonValue(command(request))).asPromise();
       default:
         return adapt(new BadRequestException("Unknown action: " + request.getAction()))
             .asPromise();
     }
   } catch (IllegalArgumentException e) {
     return adapt(new BadRequestException("Unknown action: " + request.getAction())).asPromise();
   } catch (ResourceException e) {
     return e.asPromise();
   }
 }
  /**
   * Removes the <code>Dictionary</code> for the given <code>pid</code>. If such a dictionary does
   * not exist, this method has no effect.
   *
   * @param pid The identifier of the dictionary to delet.
   * @throws IOException If an error occurrs deleting the dictionary. This exception must not be
   *     thrown if no dictionary with the given identifier exists.
   */
  public void delete(String pid) throws IOException {
    logger.debug("delete call for {}", pid);
    Object removed = tempStore.remove(pid);
    if (removed != null) {
      logger.debug("Deleted {} from temporary store", pid);
    }
    try {
      if (isReady(0) && requireRepository) {
        String id = pidToId(pid);
        boolean retry;
        String rev = null;
        do {
          retry = false;
          try {

            ReadRequest readRequest = Requests.newReadRequest(id);
            Map<String, Object> existing = repo.read(readRequest).getContent().asMap();
            if (existing != null) {
              rev = (String) existing.get("_rev");
              DeleteRequest r = Requests.newDeleteRequest(id);
              r.setRevision(rev);
              repo.delete(r);
              logger.debug("Deleted {}", pid);
            }
          } catch (PreconditionFailedException ex) {
            logger.debug("Concurrent change during delete, retrying {} {}", pid, rev);
            retry = true;
          } catch (NotFoundException ex) {
            // If it doesn't exists (anymore) that's fine
          }
        } while (retry);
      }
    } catch (ResourceException ex) {
      throw new IOException(
          "Failed to delete configuration + " + pid + " in repository: " + ex.getMessage(), ex);
    }
  }
  @Test
  public void getShouldFailWhenResourceExceptionThrown() throws ResourceException {

    // Given
    final String uri = "URI";
    final Map<String, String> queryParameters = new HashMap<String, String>();
    final Map<String, String> headers = new HashMap<String, String>();
    final org.restlet.resource.ResourceException exception =
        new org.restlet.resource.ResourceException(500, "EXCEPTION_MESSAGE", "DESCRIPTION", "URI");

    doThrow(exception).when(resource).get(JSONObject.class);

    // When
    try {
      restClient.get(uri, queryParameters, headers);
    } catch (ResourceException e) {
      // Then
      assertEquals(e.getCode(), 500);
      assertEquals(e.getMessage(), "EXCEPTION_MESSAGE");
      verify(resource, never()).addQueryParameter(anyString(), anyString());
      verify(requestHeaders, never()).set(anyString(), anyString());
      verify(resource, never()).getContext();
    }
  }
 /**
  * Adapts a {@code Throwable} to a {@code ResourceException}. If the {@code Throwable} is an JSON
  * {@code JsonValueException} then an appropriate {@code ResourceException} is returned, otherwise
  * an {@code InternalServerErrorException} is returned.
  *
  * @param t The {@code Throwable} to be converted.
  * @return The equivalent resource exception.
  */
 public ResourceException adapt(final Throwable t) {
   int resourceResultCode;
   try {
     throw t;
   } catch (OConcurrentModificationException ex) {
     resourceResultCode = ResourceException.VERSION_MISMATCH;
   } catch (final ResourceException e) {
     return e;
   } catch (final JsonValueException e) {
     resourceResultCode = ResourceException.BAD_REQUEST;
   } catch (final Throwable tmp) {
     resourceResultCode = ResourceException.INTERNAL_ERROR;
   }
   return ResourceException.getException(resourceResultCode, t.getMessage(), t);
 }
Beispiel #20
0
  /**
   * Records an 'access' audit event before and after the filtered CREST resource receives an query
   * request.
   *
   * <p>If the 'before' audit event fails due to an error, the request is cancelled and an error
   * response is returned. If the 'after' audit event fails due to an error, the request is not
   * cancelled as it's affects may have already been applied.
   *
   * @param context {@inheritDoc}
   * @param request {@inheritDoc}
   * @param handler {@inheritDoc}
   * @param next {@inheritDoc}
   */
  @Override
  public void filterQuery(
      ServerContext context,
      QueryRequest request,
      QueryResultHandler handler,
      RequestHandler next) {

    AuditingQueryResultHandler auditingHandler =
        newQueryAuditingResultHandler(context, request, handler);
    try {
      auditingHandler.auditAccessAttempt();
    } catch (AuditException e) {
      handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR));
      return;
    }

    next.handleQuery(context, request, auditingHandler);
  }
Beispiel #21
0
  /**
   * Records an 'access' audit event before and after the filtered CREST resource receives an action
   * request.
   *
   * <p>If the 'before' audit event fails due to an error, the request is cancelled and an error
   * response is returned. If the 'after' audit event fails due to an error, the request is not
   * cancelled as it's affects may have already been applied.
   *
   * @param context {@inheritDoc}
   * @param request {@inheritDoc}
   * @param handler {@inheritDoc}
   * @param next {@inheritDoc}
   */
  @Override
  public void filterAction(
      ServerContext context,
      ActionRequest request,
      ResultHandler<JsonValue> handler,
      RequestHandler next) {

    AuditingResultHandler<JsonValue> auditingHandler =
        newAuditingResultHandler(context, request, handler);
    try {
      auditingHandler.auditAccessAttempt();
    } catch (AuditException e) {
      handler.handleError(ResourceException.getException(ResourceException.INTERNAL_ERROR));
      return;
    }

    next.handleAction(context, request, auditingHandler);
  }
  /**
   * Stores the <code>Dictionary</code> under the given <code>pid</code>.
   *
   * @param pid The identifier of the dictionary.
   * @param properties The <code>Dictionary</code> to store.
   * @throws IOException If an error occurrs storing the dictionary. If this exception is thrown, it
   *     is expected, that {@link #exists(String) exists(pid} returns <code>false</code>.
   */
  public void store(String pid, Dictionary properties) throws IOException {
    logger.debug("Store call for {} {}", pid, properties);

    // Store config handling settings in memory
    if (pid.startsWith("org.apache.felix.fileinstall")) {
      tempStore.put(pid, properties);
      return;
    }

    try {
      if (isReady(0) && requireRepository) {
        String id = pidToId(pid);

        Map<String, Object> obj = dictToMap(properties);
        JsonValue content = new JsonValue(obj);
        String configResourceId =
            ConfigBootstrapHelper.getId(
                content.get(ConfigBootstrapHelper.CONFIG_ALIAS).asString(),
                content.get(ConfigBootstrapHelper.SERVICE_PID).asString(),
                content.get(ConfigBootstrapHelper.SERVICE_FACTORY_PID).asString());
        String configString = (String) obj.get(JSONEnhancedConfig.JSON_CONFIG_PROPERTY);
        Map<Object, Object> configMap = deserializeConfig(configString);
        if (configMap != null) {
          configMap.put("_id", configResourceId);
        }
        obj.put(JSONEnhancedConfig.JSON_CONFIG_PROPERTY, configMap);

        Map<String, Object> existing = null;
        try {
          ReadRequest readRequest = Requests.newReadRequest(id);
          existing = repo.read(readRequest).getContent().asMap();
        } catch (NotFoundException ex) {
          // Just detect that it doesn't exist
        }
        if (existing != null) {
          String rev = (String) existing.get("_rev");

          existing.remove("_rev");
          existing.remove("_id");
          obj.remove("_rev"); // beware, this means _id and _rev should not be in config file
          obj.remove("_id"); // beware, this means _id and _rev should not be in config file
          obj.remove(RepoPersistenceManager.BUNDLE_LOCATION);
          obj.remove(RepoPersistenceManager.FELIX_FILEINSTALL_FILENAME);
          if (!existing.equals(obj)) {
            logger.trace("Not matching {} {}", existing, obj);
            boolean retry;
            do {
              retry = false;
              try {
                UpdateRequest r = Requests.newUpdateRequest(id, new JsonValue(obj));
                r.setRevision(rev);
                repo.update(r);
              } catch (PreconditionFailedException ex) {
                logger.debug("Concurrent change during update, retrying {} {}", pid, rev);
                ReadRequest readRequest = Requests.newReadRequest(id);
                existing = repo.read(readRequest).getContent().asMap();
                retry = true;
              }
            } while (retry);
            logger.debug("Updated existing config {} {} {}", new Object[] {pid, rev, obj});
          } else {
            logger.debug(
                "Existing config same as store request, ignoring {} {} {}",
                new Object[] {pid, rev, obj});
          }
        } else {
          logger.trace("Creating: {} {} ", id, obj);
          // This may create a new (empty) configuration, which felix marks with
          // _felix___cm__newConfiguration=true
          String newResourceId = id.substring(CONFIG_CONTEXT_PREFIX.length());
          CreateRequest createRequest =
              Requests.newCreateRequest(CONFIG_CONTEXT_PREFIX, new JsonValue(obj));
          createRequest.setNewResourceId(newResourceId);
          obj = repo.create(createRequest).getContent().asMap();
          logger.debug("Stored new config in repository {} {}", pid, obj);
        }
      } else {
        tempStore.put(pid, properties);
        logger.debug("Stored in memory {} {}", pid, properties);
      }
    } catch (ResourceException ex) {
      throw new IOException("Failed to store configuration in repository: " + ex.getMessage(), ex);
    }
  }
  /**
   * Performs the query on the specified object and returns the associated results.
   *
   * <p>Queries are parametric; a set of named parameters is provided as the query criteria. The
   * query result is a JSON object structure composed of basic Java types.
   *
   * <p>The returned map is structured as follow: - The top level map contains meta-data about the
   * query, plus an entry with the actual result records. - The <code>QueryConstants</code> defines
   * the map keys, including the result records (QUERY_RESULT)
   *
   * @param context identifies the object to query.
   * @param request the parameters of the query to perform.
   * @return the query results, which includes meta-data and the result records in JSON object
   *     structure format.
   * @throws NotFoundException if the specified object could not be found.
   * @throws BadRequestException if the specified params contain invalid arguments, e.g. a query id
   *     that is not configured, a query expression that is invalid, or missing query substitution
   *     tokens.
   * @throws ForbiddenException if access to the object or specified query is forbidden.
   */
  @Override
  public Promise<QueryResponse, ResourceException> handleQuery(
      final Context context, final QueryRequest request, final QueryResourceHandler handler) {

    // If paged results are requested then decode the cookie in order to determine
    // the index of the first result to be returned.
    final int requestPageSize = request.getPageSize();

    // Cookie containing offset of last request
    final String pagedResultsCookie = request.getPagedResultsCookie();

    final boolean pagedResultsRequested = requestPageSize > 0;

    // index of first record (used for SKIP/OFFSET)
    final int firstResultIndex;

    if (pagedResultsRequested) {
      if (StringUtils.isNotEmpty(pagedResultsCookie)) {
        try {
          firstResultIndex = Integer.parseInt(pagedResultsCookie);
        } catch (final NumberFormatException e) {
          return new BadRequestException("Invalid paged results cookie").asPromise();
        }
      } else {
        firstResultIndex = Math.max(0, request.getPagedResultsOffset());
      }
    } else {
      firstResultIndex = 0;
    }

    // Once cookie is processed Queries.query() can rely on the offset.
    request.setPagedResultsOffset(firstResultIndex);

    try {
      List<ResourceResponse> results = query(request);
      for (ResourceResponse result : results) {
        handler.handleResource(result);
      }

      /*
       * Execute additional -count query if we are paging
       */
      final String nextCookie;
      // The number of results (if known)
      final int resultCount;

      if (pagedResultsRequested) {
        // count if requested
        switch (request.getTotalPagedResultsPolicy()) {
          case ESTIMATE:
          case EXACT:
            // Get total if -count query is available
            final String countQueryId = request.getQueryId() + "-count";
            if (queries.queryIdExists(countQueryId)) {
              QueryRequest countRequest = Requests.copyOfQueryRequest(request);
              countRequest.setQueryId(countQueryId);

              // Strip pagination parameters
              countRequest.setPageSize(0);
              countRequest.setPagedResultsOffset(0);
              countRequest.setPagedResultsCookie(null);

              List<ResourceResponse> countResult = query(countRequest);

              if (countResult != null && !countResult.isEmpty()) {
                resultCount = countResult.get(0).getContent().get("total").asInteger();
              } else {
                logger.debug("Count query {} failed.", countQueryId);
                resultCount = NO_COUNT;
              }
            } else {
              logger.debug("No count query found with id {}", countQueryId);
              resultCount = NO_COUNT;
            }
            break;
          case NONE:
          default:
            resultCount = NO_COUNT;
            break;
        }

        if (results.size() < requestPageSize) {
          nextCookie = null;
        } else {
          final int remainingResults = resultCount - (firstResultIndex + results.size());
          if (remainingResults == 0) {
            nextCookie = null;
          } else {
            nextCookie = String.valueOf(firstResultIndex + requestPageSize);
          }
        }
      } else {
        resultCount = NO_COUNT;
        nextCookie = null;
      }

      if (resultCount == NO_COUNT) {
        return newQueryResponse(nextCookie).asPromise();
      } else {
        return newQueryResponse(nextCookie, EXACT, resultCount).asPromise();
      }
    } catch (ResourceException e) {
      return e.asPromise();
    }
  }
  /**
   * TODO Implement this method
   *
   * <p>{@inheritDoc}
   */
  public Promise<QueryResponse, ResourceException> handleQuery(
      final Context context, final QueryRequest request, final QueryResourceHandler handler) {
    EventEntry measure =
        Publisher.start(
            Name.get(
                "openidm/internal/script/" + this.getScriptEntry().getName().getName() + "/query"),
            null,
            null);
    try {
      final ScriptEntry _scriptEntry = getScriptEntry();
      if (!_scriptEntry.isActive()) {
        throw new ServiceUnavailableException("Inactive script: " + _scriptEntry.getName());
      }
      final Script script = _scriptEntry.getScript(context);
      script.setBindings(script.createBindings());
      customizer.handleQuery(context, request, script.getBindings());

      final Function<Void> queryCallback =
          new Function<Void>() {
            @Override
            public Void call(Parameter scope, Function<?> callback, Object... arguments)
                throws ResourceException, NoSuchMethodException {
              if (arguments.length == 3 && null != arguments[2]) {
                if (arguments[2] instanceof Map) {}

                if (arguments[2] instanceof JsonValue) {

                } else {
                  throw new NoSuchMethodException(
                      FunctionFactory.getNoSuchMethodMessage("callback", arguments));
                }
              } else if (arguments.length >= 2 && null != arguments[1]) {
                if (arguments[1] instanceof Map) {}

                if (arguments[1] instanceof JsonValue) {

                } else {
                  throw new NoSuchMethodException(
                      FunctionFactory.getNoSuchMethodMessage("callback", arguments));
                }
              } else if (arguments.length >= 1 && null != arguments[0]) {
                if (arguments[0] instanceof Map) {}

                if (arguments[0] instanceof JsonValue) {

                } else {
                  throw new NoSuchMethodException(
                      FunctionFactory.getNoSuchMethodMessage("callback", arguments));
                }
              } else {
                throw new NoSuchMethodException(
                    FunctionFactory.getNoSuchMethodMessage("callback", arguments));
              }
              return null;
            }
          };
      script.putSafe("callback", queryCallback);
      Object rawResult = script.eval();
      JsonValue result = null;
      if (rawResult instanceof JsonValue) {
        result = (JsonValue) rawResult;
      } else {
        result = new JsonValue(rawResult);
      }
      QueryResponse queryResponse = newQueryResponse();
      // Script can either
      // - return null and instead use callback hook to call
      //   handleResource, handleResult, handleError
      //   careful! script MUST call handleResult or handleError itself
      // or
      // - return a result list of resources
      // or
      // - return a full query result structure
      if (!result.isNull()) {
        if (result.isList()) {
          // Script may return just the result elements as a list
          handleQueryResultList(result, handler);
        } else {
          // Or script may return a full query response structure,
          // with meta-data and results field
          if (result.isDefined(QueryResponse.FIELD_RESULT)) {
            handleQueryResultList(result.get(QueryResponse.FIELD_RESULT), handler);
            queryResponse =
                newQueryResponse(
                    result.get(QueryResponse.FIELD_PAGED_RESULTS_COOKIE).asString(),
                    result
                        .get(QueryResponse.FIELD_TOTAL_PAGED_RESULTS_POLICY)
                        .asEnum(CountPolicy.class),
                    result.get(QueryResponse.FIELD_TOTAL_PAGED_RESULTS).asInteger());
          } else {
            logger.debug("Script returned unexpected query result structure: ", result.getObject());
            return new InternalServerErrorException(
                    "Script returned unexpected query result structure of type "
                        + result.getObject().getClass())
                .asPromise();
          }
        }
      }
      return queryResponse.asPromise();
    } catch (ScriptException e) {
      return convertScriptException(e).asPromise();
    } catch (ResourceException e) {
      return e.asPromise();
    } catch (Exception e) {
      return new InternalServerErrorException(e.getMessage(), e).asPromise();
    } finally {
      measure.end();
    }
  }
 @Override
 public void updateInstance(
     ServerContext context, UpdateRequest request, ResultHandler<Resource> handler) {
   handler.handleError(ResourceException.getException(ResourceException.NOT_SUPPORTED));
 }
 @Override
 public void actionInstance(
     ServerContext context, ActionRequest request, ResultHandler<JsonValue> handler) {
   handler.handleError(ResourceException.getException(ResourceException.NOT_SUPPORTED));
 }