Ticket #934 (closed Defect: fixed)

Opened 5 years ago

Last modified 5 years ago

dm4-core: privileged call to support a passwort reset mechanism

Reported by: Malte Owned by: Malte
Priority: Major Milestone: Release 4.8
Component: DeepaMehta Standard Distribution Version: 4.7
Keywords: Cc: jri, JuergeN
Complexity: 3 Area: Application Framework / API
Module: deepamehta-accesscontrol

Description

Just needed (myDM, Kiezatlas 2 and many others).

Enable the sign-up plugin to implement a passwort reset mechanism. Access to such should be realized with the mailing feature and a token sent out. The tokenized URL then gives access to a UI where such operation can be performed by the mailbox owner.

Change History

comment:1 Changed 5 years ago by Malte

  • Module set to deepamehta-accesscontrol

Hey JuergeN, this ticket should cover what you were mentioning to me lately for myDM.

comment:2 Changed 5 years ago by jri

  • Owner set to jri
  • Status changed from new to accepted

comment:3 Changed 5 years ago by Jörg Richter <jri@…>

In b7ee6f7f2f49bb4e173ce577a15b27949e9b9ae9/deepamehta:

Core: add privileged change-password call (#934).

1 new method in Core AccessControl:

void changePassword(Credentials cred)

Changes the password of an existing user account. (The username can't be changed.)
An user account with the given username must exist.
This is a privileged method: it works also if the respective user is not logged in.

The DM standard distro provides no REST API for this method.
It's up to a 3rd-party plugin to establish a proprietary authorization procedure before calling this method.

Hint: there are 2 approaches to instantiate a Credentials object:

  • Instantiation through JAX-RS: transport a JSON object in the message body ...
    {
      username: "john",
      password: "-SHA256-encoded-password"    # the -SHA256- prefix is mandatory
    }
    

... and receive it in your resource method as an entity parameter:

import de.deepamehta.core.service.accesscontrol.Credentials;

@GET/PUT
@Path("/your-path")
@Transactional
public void yourResourceMethod(Credentials cred) {
	...
}
  • Instantiating yourself:
    new Credentials("john", "plaintext-password")
    

Note that the password is expected as plain text in this case.
SHA256-encoding is done by DM.

See #934.

comment:4 Changed 5 years ago by Malte

Ok, thanks for the support!

comment:5 Changed 5 years ago by Malte

Since the "Email Address" topics are now part of the "Administration" workspace, processing the results of a "Password Reset" Dialog (asking for Username and Email) cannot investigate IF there is a specific association between the given Username and Email topic.

Thus, the password reset mail with a link is sent out if (1) the given username exists and (2) the given email address exists. By the state of the current myDM ACL/Workspace setup ("Email" topics end up in the "Collaborative" administration workspace) no further validation (e.g. if email and username do correlate) is possible.

BTW: This setup, assigning all Email Address topics to a "Collaborative" workspace also hinders the subscription plugin (see #825) to send out notifications to users mailboxes no behalf of their subscriptions.

Just to let you know: I think that the new "password reset" feature of the dm4-sign-up module should just be active/working IF the administrator has set the "Email based confirmation workflow" option (of the "Sign-up Configuration topic") to "true".

Last edited 5 years ago by Malte (previous) (diff)

comment:6 Changed 5 years ago by Malte

Moved comment to where it belongs #825.

Last edited 5 years ago by Malte (previous) (diff)

comment:7 Changed 5 years ago by Malte

I revoke my thoughts on the current password reset authentication model i just proposed in comment:5.

Such an implementation, of course, would enable other (alread registered dm4-) users to send themselves password reset links corresponding to other people user accounts (just through providing their own Email Address in the password-reset dialog (since it would be an existing E-mail Address).

Thus, i currently have no idea to implement a password-reset authentication without relating (and thus validating) the email address to a username. In this case, i think, it would be enough if we would have a call like "hasRelatedTopic("dm4.contacts.email_address", eMailAddressValue)-call just to check IF there is a relation between the provided username and emailAddressValue.

Such a solution would be enough for the envisioned password-reset feature but then, see also the request for needing access to concrete emailAddressValues directly as commented in #825comment:5.

Thanks for your help.

comment:8 Changed 5 years ago by Malte

Just to let you know how the email address and username currently are related to each other by d4m-sign-up:

Topic emailTopic = usernameTopic.getRelatedTopic("org.deepamehta.signup.user_mailbox",
        "dm4.core.parent", "dm4.core.child", "dm4.contacts.email_address");

That request throws a 401 since "email_address" now resides in the "Administration" workspace.

comment:9 Changed 5 years ago by Malte

The immediate solution (and only currently available answer) to this (as well as to solving issue #825) is to store "Email Address" topics in the public "System" workspace again.

comment:10 Changed 5 years ago by jri

Yes, I understand the problem.
Also in regard of the subscription module (#825).

A solution would be to add another privileged method:

String getEmailAddress(String username)

My only objection would be that the Core then virtually depends on both, the Sign-up and the Contacts modules (as it would refer to the org.deepamehta.signup.user_mailbox and dm4.contacts.email_address types) what theoretically would create a circular dependency (as all modules depend on Core). However, this would be a "soft" dependency and not a technical problem.

Should we go this way?
Would the above method be sufficient?

BTW: I would tend to keep the Email Address topics in the Administration workspace (and not move to System) to maintain the privacy aspect: a logged in user is not supposed to see the email addresses of all users.

Thank you for advancing with the Reset Password feature!

comment:11 Changed 5 years ago by jri

I like to take a step back here and think about the Reset Password mechanism in general.

If a user has forgotten her password she possibly has forgotten hers username as well, right?

I think the common Reset Password mechanism only requires the user to enter hers email address, not the username. I'm right?

To me it would make sense to design the Reset Password mechanism in this way.

The required privileged API would be look different in this case.

comment:12 follow-up: ↓ 13 Changed 5 years ago by Malte

Thanks for sharing your thoughts.

Regarding the Reset Password mechanism, OK, fine, lets' do it this way. Email Addresses will be assigned to the Administration Workspace by the sign-up plugin.

So, to send out a "Password reset link" the current API would then be sufficient ("using dm4.getAccessControl().emailAddressExists(String emailAddressValue)").

But to reset the correct password i need to fetch the full "Email Address" topic (as part of a request by anonymous) in order to be able to set the new credentials for the corresponding "username" involved (and related to the emailAddressValue as described in comment:8). So, a call like "Topic getEmailAddressTopic(String emailAddressValue)" would be needed.

And yes, from my point of view on the subscriptions case (#825), an API extension like "String getEmailAddress(String username)" would be what is needed if we want to built the support for "notifications" via mail.

comment:13 in reply to: ↑ 12 Changed 5 years ago by jri

Replying to Malte:

But to reset the correct password i need to fetch the full "Email Address" topic (as part of a request by anonymous) in order to be able to set the new credentials for the corresponding "username" involved (and related to the emailAddressValue as described in comment:8). So, a call like "Topic getEmailAddressTopic(String emailAddressValue)" would be needed.

I think you're making a mistake here.

You have an email address (String) at hand and need to query the corresponding username, right?

Even if you had the "Email Address" topic, you could not navigate to the Username topic as anonymous.

Username topics are assigned to the System workspace. Remember, despite the System workspace is public it is readable for logged in users only. (The System workspace is special in this regard.)

So, what the Reset Password mechanism requires is a privileged method:

String getUsername(String emailAddress)

And the counterpart is still needed for the subscriptions case (#825):

String getEmailAddress(String username)

If you agree I would implement these methods soon.

comment:14 Changed 5 years ago by Malte

Thanks, you're absolutely correct. So, great, let's go for that!

comment:15 Changed 5 years ago by jri

OK :-)

comment:16 Changed 5 years ago by Jörg Richter <jri@…>

In 47724cce2eac84e5819910dc8d301a5320ce2563/deepamehta:

Core: username/email privileged calls (#934).

2 new methods in Core's AccessControl:

String getUsername(String emailAddress);

Returns the username for the given email address.

The username is determined by traversing from the Email Address topic along a
org.deepamehta.signup.user_mailbox association.

This is a privileged method, it bypasses the access control system.

@throws RuntimeException?
if no such Email Address topic exists in the DB, or
if more than one such Email Address topics exist in the DB, or
if the Email Address topic is not associated to a Username topic.

String getEmailAddress(String username);

Returns the email address for the given username.

The email address is determined by traversing from the Username topic along a
org.deepamehta.signup.user_mailbox association.

This is a privileged method, it bypasses the access control system.

@throws RuntimeException?
if no such Username topic exists in the DB, or
if the Username topic is not associated to an Email Address topic.

See #934.

comment:17 Changed 5 years ago by jri

https://github.com/jri/deepamehta/commit/6c988731

Core Access Control: revise username/email (#934).

2 Core AccessControl calls are revised:

String getUsername(String emailAddress)

In case of more than one Email Address topics exist, the one that is assigned to a Username topic is returned.
Formerly an exception was thrown.

boolean emailAddressExists(String emailAddress)

True is returned only if the email address is actually assigned to a Username topic.

See #934.

comment:18 Changed 5 years ago by Malte

  • Owner changed from jri to Malte

Do you want me to extend the login dialog provided by the dm4-webclient (somehow from with the dm4-sign-up module JS) to provide a message and link, like:

Forgot your password?
<link>Password reset</link>

comment:19 Changed 5 years ago by jri

Yes, very good.
Tell me if you need support at side of dm4-webclient.

comment:20 Changed 5 years ago by Malte

Ok, i got it working. I adapted the init_3 hook of my js plugin to listen to any jQuery UI Dialog opend by the webclient document. It would be nice if the login dialog had an ID but that is not required as i identify it by the existence of the #login-message element.

 dm4c.add_listener("init_3", function() {
        ..
        $(document).on("dialogopen", function(event, ui) {
            var $login_message = $("#login-message", event.target)
            if ($login_message.length > 0) {
                $('<br/><span class="password-reset">Forgot password?<br/>'
                    + '<a href="/sign-up/request-password">Password reset</a></span>')
                    .insertAfter($('.ui-dialog .ui-dialog-buttonset'))
            }
        })
    })

Assuming that only one jQuery Dialog should be open at once and assuming that #login-message is only contained in the dialog i want to adapt, this should be fine.

comment:21 Changed 5 years ago by jri

Nice hack using the dialogopen event :-)

comment:22 Changed 5 years ago by jri

  • Status changed from accepted to closed
  • Resolution set to fixed
Note: See TracTickets for help on using tickets.