Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for master key clients to create user sessions #7406

Conversation

GormanFletcher
Copy link
Contributor

@GormanFletcher GormanFletcher commented May 28, 2021

New Pull Request Checklist

Issue Description

This PR proposes an implementation for #6641: a way for Cloud Code (or any master key client) to create user sessions without access to the user's credentials, and without presumption of an existing user session. This is useful for user impersonation (customer support), or for creating a standalone API that can act with a user's authority after validating the user's identity some other way (e.g., API key).

Related issue: #6641

Approach

I've created a new REST endpoint that creates a new session for the given user ID if the master key is supplied. I elided the before/after login triggers since it's not really a login - I assume we want a clear semantic distinction between normal user activity and e.g., a user making an API call against their account that creates a session, or a customer support representative logging in as the user to investigate a ticket. Maybe a parameter in the API call should toggle hooks on/off?

TODOs before merging

Still needs docs updates, and I'll submit a PR for the JS SDK, both after I'm given the thumbs-up on the new API.

  • Add test cases
  • Add entry to changelog
  • Add changes to documentation (guides, repository pages, in-code descriptions)
  • Add new Parse Error codes to Parse JS SDK?
    • Not sure if I'm using the existing error codes correctly.
  • What authProvider should the session be flagged with? Is password okay, or do we need a new value?

Copy link
Contributor

@sadortun sadortun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is awesome!

I suggest the REST endpoint to be renamed to impersonateUser

src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
@mtrezza mtrezza changed the title 6641: Implement support for master key clients to create user sessions Add support for master key clients to create user sessions May 29, 2021
@mtrezza
Copy link
Member

mtrezza commented May 29, 2021

Add security check
I wasn't sure what to do here; the existing auth routes don't use CheckGroups, but I did see they check req.isMaster, so I used that as a starting point.

I don't think a security check is needed here. You are not adding a configurable feature / new option to Parse Server. Security checks evaluate options that the developer has configured and that may create points of attacks if not configured properly.

If you agree that no security check is needed, you may well delete the TODO from the template.

Still needs docs updates, and I'll submit a PR for the JS SDK, both after I'm given the thumbs-up on the new API.

This should be a TODO in the related issue, not in this PR. Since the issue has not been created by you (and is using an old template), you could just add it as a comment or create a new issue and reference the existing issue. The issue itself should contain all TODOs that are necessary for this feature, such as:

  • Parse Server PR
  • REST API doc change

Any implementation in the Parse JS SDK (or any other Parse client SDK) could be regarded as a separate undertaking, each with its own required TODO in the respective SDK guide.

Should the fn return the Session, or the session token?

The REST response should be the same response as for the /login endpoint. This allows to leverage existing logic in Parse Server and for the developer in custom code.

src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
@GormanFletcher
Copy link
Contributor Author

I've created a pull request to the docs repo covering the new endpoint.

@GormanFletcher
Copy link
Contributor Author

What's the appropriate way to handle locking out a user from the /loginAs endpoint? If a user's account is locked out, we wouldn't want them being allowed to hit a custom API endpoint that performs a /loginAs. I could make /loginAs perform the same ACL check that /login makes, but then we'd need a way to signify whether the request should override the lockout. /login marks the difference by the presence of the master key, but that won't work for /loginAs since it always requires the master key.

Should /loginAs simply take another parameter, honorLockout, defaulting to true?

@mtrezza
Copy link
Member

mtrezza commented Jun 2, 2021

If a user's account is locked out, we wouldn't want them being allowed to hit a custom API endpoint that performs a /loginAs.

I hope I understand your question correctly. Security of a custom API endpoint is up to the developer, whatever their use case requires.

Which account do you mean would be locked - the user who invokes loginAs or the user that is returned by the request?

@GormanFletcher
Copy link
Contributor Author

GormanFletcher commented Jun 2, 2021

I hope I understand your question correctly. Security of a custom API endpoint is up to the developer, whatever their use case requires.

Which account do you mean would be locked - the user who invokes loginAs or the user that is returned by the request?

Should /loginAs forbid logging into a user account whose ACL is master-key only, the same as /login does, since they're both named 'login'? Or should /loginAs allow logging into a locked-out account, since the master key is always supplied?

I could go either way - I just need to know whether I should make a code change to this PR to allow supplying a parameter to /loginAs to honor or bypass a lockout, or an annotation in the docs PR that developers need to manually check for a locked-out user when using /loginAs.

@mtrezza
Copy link
Member

mtrezza commented Jun 2, 2021

The master key always trumps any restriction and ACL.

loginAs should always succeed with master key, because if it does not, how else would one override that restriction, if not - again - with the master key? If there is any check required, then the developer can create a Cloud Function that conditionally calls loginAs.

@GormanFletcher
Copy link
Contributor Author

Got it. I've updated my REST docs PR with that information.

This PR still has two outstanding questions:

  • Confirmation that I'm using the correct error codes in each situation
  • What's the appropriate authProvider value to use? I don't know if this behavior is a match for any existing values. The PR currently uses anonymous.

Other than that, are there any outstanding concerns before this PR could be considered for merge?

@mtrezza
Copy link
Member

mtrezza commented Jun 2, 2021

Confirmation that I'm using the correct error codes in each situation

Can you list the error codes and the scenarios? I'll take a look in the changes.

What's the appropriate authProvider value to use? I don't know if this behavior is a match for any existing values. The PR currently uses anonymous.

I think we can introduce a new one, otherwise we cannot differentiate the session origin. Any suggestions?

@GormanFletcher
Copy link
Contributor Author

Can you list the error codes and the scenarios?

If the master key isn't supplied, OPERATION_FORBIDDEN is thrown.

If the REST call doesn't supply the userId parameter, MISSING_OBJECT_ID is thrown.

If there isn't a user with the supplied userId, OBJECT_NOT_FOUND is thrown. This seemed more specific than INVALID_VALUE.

What's the appropriate authProvider value to use? I don't know if this behavior is a match for any existing values. The PR currently uses anonymous.

I think we can introduce a new one, otherwise we cannot differentiate the session origin. Any suggestions?

How about:

  • masterkey
  • admin
  • impersonate

@mtrezza
Copy link
Member

mtrezza commented Jun 2, 2021

Looking at the current auth provider terms for an indication, I think masterkey most accurately describes what facilitated the authentication. Compare to password for example.

However, masterkey is also a rather primitive, technical term. It does not suggest that the master key was used as an override, with would be more analogous to anonymous.

So for lack of a better term (admin and impersonation being too abstract I think) we could go with masterkey or override (or maybe something along that line).

@GormanFletcher
Copy link
Contributor Author

I've pushed a change setting the value to override.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Let's give the auth provider naming some more thought, since this is unlikely because difficult to change in the future.

Copy link
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🙌 for cleaning up and code documentation

Just some corrections for the error codes and testing.

spec/ParseUser.spec.js Outdated Show resolved Hide resolved
src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
spec/ParseUser.spec.js Outdated Show resolved Hide resolved
src/Routers/UsersRouter.js Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented Jun 3, 2021

Codecov Report

Merging #7406 (3a2726d) into master (754c127) will decrease coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #7406      +/-   ##
==========================================
- Coverage   93.95%   93.93%   -0.02%     
==========================================
  Files         181      181              
  Lines       13228    13245      +17     
==========================================
+ Hits        12428    12442      +14     
- Misses        800      803       +3     
Impacted Files Coverage Δ
src/Routers/UsersRouter.js 94.38% <100.00%> (+0.59%) ⬆️
src/batch.js 91.37% <0.00%> (-1.73%) ⬇️
src/Adapters/Files/GridFSBucketAdapter.js 79.50% <0.00%> (-0.82%) ⬇️
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 92.59% <0.00%> (-0.66%) ⬇️
src/RestWrite.js 93.92% <0.00%> (-0.16%) ⬇️
...dapters/Storage/Postgres/PostgresStorageAdapter.js 95.51% <0.00%> (+0.15%) ⬆️
src/ParseServerRESTController.js 98.50% <0.00%> (+1.49%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 754c127...3a2726d. Read the comment docs.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Could you take a look at the coverage report to see whether this PR requires more tests?

@GormanFletcher
Copy link
Contributor Author

The coverage report seems off to me. Codecov thinks all coverage for the Redis adapter has been removed, as well as some cases for the Mongo adapter (e.g., handling DB connection/auth errors) and some RestWrite cases (username taken, email taken). Reviewing the PR diff, I don't see how it could be affecting coverage for those subsystems.

Coverage for UsersRouter is up 0.59%.

But the CI checks don't mark the coverage as a requirement. It looks like CI failed on the self-check with this error:

Error: CI environments are not up-to-date with the latest Node.js versions.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

The coverage report seems off to me.

Could be false positives.

Error: CI environments are not up-to-date with the latest Node.js versions.

I'll submit a PR to fix this.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

If you rebase on master, the CI tests should pass.

@GormanFletcher GormanFletcher force-pushed the create-session-without-authentication branch from 8924dff to 783681b Compare June 3, 2021 22:24
…n log in as any user, without access to the user's credentials, and without presuming the user already has a session
@GormanFletcher
Copy link
Contributor Author

I've rebased onto master.

@mtrezza
Copy link
Member

mtrezza commented Jun 3, 2021

Can you please delete the package-lock file and rebuild it? It's out of sync with package.json.

@GormanFletcher GormanFletcher force-pushed the create-session-without-authentication branch from 783681b to 303c58d Compare June 3, 2021 23:17
@GormanFletcher
Copy link
Contributor Author

Done.

Copy link
Member

@mtrezza mtrezza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for this great feature!

@GormanFletcher
Copy link
Contributor Author

Thanks so much for all your help!

Copy link
Member

@dplewis dplewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you revert the package-lock file? Otherwise this looks good to me!

@mtrezza
Copy link
Member

mtrezza commented Jun 4, 2021

Why revert? I rebuilt it from package json.

@dplewis
Copy link
Member

dplewis commented Jun 4, 2021

It doesn't have anything to do with the PR and we don't know what those dozens of dependencies updates will cause.

@mtrezza
Copy link
Member

mtrezza commented Jun 4, 2021

I see, I agree it makes sense to do this in a separate PR.

What is our current practice for this? I can't remember an explicit PR where we only updated package-lock in the past. I do remember PRs however were we regenerated the package-lock as a side effect, even though it had nothing to do with the PR.

@dplewis
Copy link
Member

dplewis commented Jun 4, 2021

This usually happens because a user deleted the package-lock file then regenerated it, thats my issue.

In this PR rearranging the package.json (npm install) shouldn't have caused a change in the lock file (I just tried it locally and no change).

@mtrezza
Copy link
Member

mtrezza commented Jun 4, 2021

makes sense, reverted package-lock

So should I regenerate the package-lock file in a dedicated PR or how to we rebuild the package-lock file?

Copy link
Member

@dplewis dplewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@dplewis
Copy link
Member

dplewis commented Jun 4, 2021

So should I regenerate the package-lock file in a dedicated PR or how to we rebuild the package-lock file?

Let Synx manage the package-lock file or if someone installs a new package the lock file will update without regenerating.

@dplewis dplewis merged commit 129f7bf into parse-community:master Jun 4, 2021
sakibguy added a commit to sakibguy/parse-server that referenced this pull request Jun 5, 2021
Add support for master key clients to create user sessions (parse-community#7406)
@mtrezza
Copy link
Member

mtrezza commented Jun 5, 2021

Let Synx manage the package-lock file or if someone installs a new package the lock file will update without regenerating.

I am not sure about that approach. I opened #7417 to discuss that.

@vince1995
Copy link
Contributor

Cool PR! But

I elided the before/after login triggers since it's not really a login

But we would use this to login users with our own login provider, so for us it's a real login. So, wouldn't it be better if it's optional to call the triggers?

@mtrezza
Copy link
Member

mtrezza commented Jun 30, 2021

@vince1995 If you want to use a custom login provider, you would create a custom auth adapter.

Please feel free to open a new issue where you explain what you suggest to change. This PR is closed.

@sadortun
Copy link
Contributor

sadortun commented Jun 30, 2021

@vince1995 @mtrezza This is related : #6641

@tehsunnliu
Copy link

Hi, any chances of this PR being released? I'm using the currently using master branch to use this feature.

@dblythy
Copy link
Member

dblythy commented Sep 27, 2021

@tehsunnliu the next major release will be V5, available in the coming months.

See: #7492

@tehsunnliu
Copy link

Thank you for the info.

@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 5.0.0-beta.1

@parseplatformorg parseplatformorg added the state:released-beta Released as beta version label Nov 1, 2021
jenwich added a commit to buddyreview/parse-server that referenced this pull request Dec 15, 2021
@mtrezza mtrezza mentioned this pull request Mar 12, 2022
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 5.0.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released Released as stable version state:released-beta Released as beta version
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants