From deca3939ae5482b660aa10bb1bcfefeafd46e043 Mon Sep 17 00:00:00 2001
From: Matthew Swift <matthew.swift@forgerock.com>
Date: Thu, 23 Jun 2016 00:21:25 +0000
Subject: [PATCH] OPENDJ-2789 Split passwordModify into distinct requests: modifyPassword and resetPassword

---
 opendj-server-legacy/resource/config/rest2ldap/endpoints/api/users-and-groups-v1.json                     |    2 
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Action.java                                 |   15 +++----
 opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties                   |   10 +++--
 opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceImpl.java                        |   54 +++++++++++++++++++++-----
 opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/users-and-groups-v1.json |    2 
 5 files changed, 58 insertions(+), 25 deletions(-)

diff --git a/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/users-and-groups-v1.json b/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/users-and-groups-v1.json
index dbdfe7d..9e11c37 100644
--- a/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/users-and-groups-v1.json
+++ b/opendj-rest2ldap-servlet/src/main/webapp/WEB-INF/classes/rest2ldap/endpoints/api/users-and-groups-v1.json
@@ -74,7 +74,7 @@
         "frapi:opendj:rest2ldap:user:1.0": {
             "superType": "frapi:opendj:rest2ldap:object:1.0",
             "objectClasses": [ "person", "organizationalPerson", "inetOrgPerson" ],
-            "supportedActions": [ "passwordModify" ],
+            "supportedActions": [ "modifyPassword", "resetPassword" ],
             "properties": {
                 "_id": {
                     "type": "simple",
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Action.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Action.java
index 1cd7ee6..21d6bae 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Action.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/Action.java
@@ -21,15 +21,14 @@
  * supported.
  */
 public enum Action {
-    // Notes:
-    //
-    // - actions are likely to become an extension point in future versions of Rest2Ldap, in which case this enum
-    //   will need to be converted into a regular class or interface,
-    //
-    // - the actions are named so that they can be parsed easily from JSON.
+    // Note: actions are likely to become an extension point in future versions of Rest2Ldap, in which case this enum
+    // will need to be converted into a regular class or interface,
 
-    /** An action that allows users to change or reset their password. */
-    PASSWORDMODIFY("passwordModify");
+    /** An action that allows users to change their password. */
+    MODIFY_PASSWORD("modifyPassword"),
+
+    /** An action that allows users to reset their password. */
+    RESET_PASSWORD("resetPassword");
 
     private final String actionId;
 
diff --git a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceImpl.java b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceImpl.java
index a1da30e..6cd3367 100644
--- a/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceImpl.java
+++ b/opendj-rest2ldap/src/main/java/org/forgerock/opendj/rest2ldap/SubResourceImpl.java
@@ -159,8 +159,10 @@
             final Action action = asEnum(request.getAction(), Action.class);
             if (resource.hasSupportedAction(action)) {
                 switch (action) {
-                case PASSWORDMODIFY:
-                    return passwordModify(context, resourceId, request);
+                case RESET_PASSWORD:
+                    return resetPassword(context, resourceId, request);
+                case MODIFY_PASSWORD:
+                    return modifyPassword(context, resourceId, request);
                 }
             }
         } catch (final IllegalArgumentException ignored) {
@@ -170,7 +172,41 @@
 
     }
 
-    private Promise<ActionResponse, ResourceException> passwordModify(
+    private Promise<ActionResponse, ResourceException> resetPassword(
+            final Context context, final String resourceId, final ActionRequest request) {
+        if (!context.containsContext(ClientContext.class)
+                || !context.asContext(ClientContext.class).isSecure()) {
+            return newResourceException(FORBIDDEN, ERR_PASSWORD_RESET_SECURE_CONNECTION.get().toString()).asPromise();
+        }
+        if (!context.containsContext(SecurityContext.class)
+                || context.asContext(SecurityContext.class).getAuthenticationId() == null) {
+            return newResourceException(FORBIDDEN, ERR_PASSWORD_RESET_USER_AUTHENTICATED.get().toString()).asPromise();
+        }
+
+        final Connection connection = connectionFrom(context);
+        return resolveResourceDnAndType(context, connection, resourceId, null)
+                .thenAsync(new AsyncFunction<RoutingContext, PasswordModifyExtendedResult, ResourceException>() {
+                    @Override
+                    public Promise<PasswordModifyExtendedResult, ResourceException> apply(RoutingContext dnAndType) {
+                        final PasswordModifyExtendedRequest pwdModifyRequest =
+                                newPasswordModifyExtendedRequest().setUserIdentity("dn: " + dnAndType.getDn());
+                        return connection.extendedRequestAsync(pwdModifyRequest)
+                                         .thenCatchAsync(adaptLdapException(PasswordModifyExtendedResult.class));
+                    }
+                }).thenAsync(new AsyncFunction<PasswordModifyExtendedResult, ActionResponse, ResourceException>() {
+                    @Override
+                    public Promise<ActionResponse, ResourceException> apply(PasswordModifyExtendedResult r) {
+                        final JsonValue result = new JsonValue(new LinkedHashMap<>());
+                        final byte[] generatedPwd = r.getGeneratedPassword();
+                        if (generatedPwd != null) {
+                            result.put("generatedPassword", valueOfBytes(generatedPwd).toString());
+                        }
+                        return newActionResponse(result).asPromise();
+                    }
+                });
+    }
+
+    private Promise<ActionResponse, ResourceException> modifyPassword(
             final Context context, final String resourceId, final ActionRequest request) {
         if (!context.containsContext(ClientContext.class)
                 || !context.asContext(ClientContext.class).isSecure()) {
@@ -185,8 +221,8 @@
         final String oldPassword;
         final String newPassword;
         try {
-            oldPassword = jsonContent.get("oldPassword").asString();
-            newPassword = jsonContent.get("newPassword").asString();
+            oldPassword = jsonContent.get("oldPassword").required().asString();
+            newPassword = jsonContent.get("newPassword").required().asString();
         } catch (JsonValueException e) {
             final LocalizableMessage msg = ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID.get();
             final ResourceException ex = newBadRequestException(msg, e);
@@ -209,12 +245,8 @@
                 }).thenAsync(new AsyncFunction<PasswordModifyExtendedResult, ActionResponse, ResourceException>() {
                     @Override
                     public Promise<ActionResponse, ResourceException> apply(PasswordModifyExtendedResult r) {
-                        final JsonValue result = new JsonValue(new LinkedHashMap<>());
-                        final byte[] generatedPwd = r.getGeneratedPassword();
-                        if (generatedPwd != null) {
-                            result.put("generatedPassword", valueOfBytes(generatedPwd).toString());
-                        }
-                        return newActionResponse(result).asPromise();
+                        // Empty response.
+                        return newActionResponse(new JsonValue(new LinkedHashMap<>(0))).asPromise();
                     }
                 });
     }
diff --git a/opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties b/opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties
index befdf97..3f56fc3 100644
--- a/opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties
+++ b/opendj-rest2ldap/src/main/resources/org/forgerock/opendj/rest2ldap/rest2ldap.properties
@@ -100,13 +100,13 @@
 ERR_CLIENT_PROVIDED_RESOURCE_ID_MISSING_54=Resources cannot be created without a client provided resource ID
 ERR_NOT_YET_IMPLEMENTED_55=Not yet implemented
 ERR_ACTION_NOT_SUPPORTED_56=The action '%s' is not supported
-ERR_PASSWORD_MODIFY_SECURE_CONNECTION_57=Password modify requires a secure connection
-ERR_PASSWORD_MODIFY_USER_AUTHENTICATED_58=Password modify requires user to be authenticated
+ERR_PASSWORD_MODIFY_SECURE_CONNECTION_57=Passwords can only be modified using a secure connection
+ERR_PASSWORD_MODIFY_USER_AUTHENTICATED_58=Passwords can only be modified by authenticated users
 ERR_DECODING_CONTROL_59=Unable to decode ldap control: '%s'
 ERR_ERROR_RESPONSE_60=An error occurred while processing the request '%s': '%s' (details: '%s')
 ERR_RUNTIME_EXCEPTION_61=A runtime exception occurred wile processing the request '%s': '%s'
-ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID_62=The password modify request has been rejected because it is invalid. \
- A password modify request may contain two string valued fields 'oldPassword' and 'newPassword'
+ERR_PASSWORD_MODIFY_REQUEST_IS_INVALID_62=The modify password request has been rejected because it is invalid. \
+ A modify password request must contain two string valued fields 'oldPassword' and 'newPassword'
 ERR_CONFIG_INVALID_TRUST_MANAGER_63=The trust-manager defined in '%s' is invalid: %s
 ERR_CONFIG_INVALID_KEY_MANAGER_64=The key-manager defined in '%s' is invalid: %s
 ERR_MISSING_TYPE_PROPERTY_IN_CREATE_65=The resource cannot be created because it does not contain the \
@@ -136,3 +136,5 @@
   directory or cannot be read
 INFO_REST2LDAP_STARTING_80=Rest2Ldap starting with configuration directory '%s'
 INFO_REST2LDAP_CREATING_ENDPOINT_81=Rest2Ldap created endpoint '%s' version %s
+ERR_PASSWORD_RESET_SECURE_CONNECTION_82=Passwords can only be reset using a secure connection
+ERR_PASSWORD_RESET_USER_AUTHENTICATED_83=Passwords can only be reset by authenticated users
diff --git a/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/users-and-groups-v1.json b/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/users-and-groups-v1.json
index dbdfe7d..9e11c37 100644
--- a/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/users-and-groups-v1.json
+++ b/opendj-server-legacy/resource/config/rest2ldap/endpoints/api/users-and-groups-v1.json
@@ -74,7 +74,7 @@
         "frapi:opendj:rest2ldap:user:1.0": {
             "superType": "frapi:opendj:rest2ldap:object:1.0",
             "objectClasses": [ "person", "organizationalPerson", "inetOrgPerson" ],
-            "supportedActions": [ "passwordModify" ],
+            "supportedActions": [ "modifyPassword", "resetPassword" ],
             "properties": {
                 "_id": {
                     "type": "simple",

--
Gitblit v1.10.0