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

Readonly trigger for _Session hook #5155

Closed

Conversation

georgesjamous
Copy link
Contributor

Allowing a ReadOnly trigger on the session targetting this workflow:

beforeSave: triggered whenever a session is created.
Thrown errors during signup are ignored; to cancel signup a hook on _User can be used.
Thrown errors during login prevent the user from login in, a session will no be created.
Any edits to the session object will be discarded.

beforeDelete: thrown errors are discarded.

no restrictions on afterSave or afterDelete.

beforeFind and afterFind are not allowed on _Session. (not changed from before)

@flovilmart does this seem okay?

};

// Returns an updated copy of the object
RestWrite.prototype.buildUpdatedObject = function(extraData) {
const updatedObject = triggers.inflate(extraData, this.originalData);
let updatedObject = triggers.inflate(extraData, this.originalData);
Object.keys(this.data).reduce(function(data, key) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I did not really understand what is the purpose of this reduce.
Didn't touch it, but RestWrite.prototype.buildUpdatedObject = function(extraData) { could be fixed to have less duplicated code (like inflating twice in the case of session)

Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert to const

@codecov
Copy link

codecov bot commented Oct 29, 2018

Codecov Report

Merging #5155 into master will decrease coverage by 0.12%.
The diff coverage is 86.66%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #5155      +/-   ##
==========================================
- Coverage   93.93%   93.81%   -0.13%     
==========================================
  Files         123      123              
  Lines        8975     8954      -21     
==========================================
- Hits         8431     8400      -31     
- Misses        544      554      +10
Impacted Files Coverage Δ
src/triggers.js 93.15% <83.33%> (-1.17%) ⬇️
src/RestWrite.js 92.79% <91.66%> (-0.46%) ⬇️
src/LiveQuery/Client.js 97.43% <0%> (-2.57%) ⬇️
src/Adapters/Storage/Mongo/MongoStorageAdapter.js 91.48% <0%> (-0.73%) ⬇️
src/Adapters/Storage/Mongo/MongoCollection.js 97.29% <0%> (-0.14%) ⬇️
src/LiveQuery/ParseLiveQueryServer.js 87.46% <0%> (-0.12%) ⬇️
...dapters/Storage/Postgres/PostgresStorageAdapter.js 97.08% <0%> (-0.08%) ⬇️

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 2d7b992...999ac90. Read the comment docs.

flovilmart
flovilmart previously approved these changes Oct 29, 2018
Copy link
Contributor

@flovilmart flovilmart left a comment

Choose a reason for hiding this comment

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

Why do we treat session creation differently when singing up than in logging in?

src/RestWrite.js Outdated
@@ -1482,20 +1482,25 @@ RestWrite.prototype.objectId = function() {
};

// Returns a copy of the data and delete bad keys (_auth_data, _hashed_password...)
RestWrite.prototype.sanitizedData = function() {
RestWrite.prototype.sanitizedData = function(decodeData = true) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I don’t recommend passing default paramètres as truths, as different implementations with bottom values will produce different results (undefined, null,false)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i will set it to false by default, but didn't know if its being used somewhere else. Was afraid from breaking something. Tests will show anyhow.

Copy link
Contributor

Choose a reason for hiding this comment

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

False / undefined /null should be the default behavior. If true is passed, then we should use the new code path

};

// Returns an updated copy of the object
RestWrite.prototype.buildUpdatedObject = function(extraData) {
const updatedObject = triggers.inflate(extraData, this.originalData);
let updatedObject = triggers.inflate(extraData, this.originalData);
Object.keys(this.data).reduce(function(data, key) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please revert to const

src/RestWrite.js Outdated
// This is okay, since sessions are mostly never updated, only created or destroyed.
// Additionally sanitizedData should be kept in json, not decoded.
// because '.inflate' internally uses '.fromJSON' so it expects data to be JSON to work properly.
updatedObject = triggers.inflate(extraData, this.sanitizedData(false));
Copy link
Contributor

Choose a reason for hiding this comment

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

Please do an early return instead

src/triggers.js Outdated
@@ -31,7 +37,7 @@ const baseStore = function() {
};

function validateClassNameForTriggers(className, type) {
const restrictedClassNames = ['_Session'];
const restrictedClassNames = [];
if (restrictedClassNames.indexOf(className) != -1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What’s the point of keeping that? Perphas use ‘readOnlyClassNames’ instead

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no point, will remove it

src/triggers.js Outdated
// handle special readOnlyTriggers cases
if (isReadOnlyTrigger) {
// Ignore thrown errors in beforeDelete & during login.
// We should prevent login from breaking if an error is thrown during the process,
Copy link
Contributor

Choose a reason for hiding this comment

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

If it’s a readOnlyTrigger, why would we perform additional checks?

If the trigger is a simple event based one errors should simply get ignored. So it should resolved always.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to give the possibility to reject a login, rejecting all readOnlyTrigger events would get in the way of that.
I am thinking now of refactoring the name to sessionTrigger rather than readonlyTrigger, it seems more appropriate.

@georgesjamous
Copy link
Contributor Author

georgesjamous commented Oct 29, 2018

Why do we treat session creation differently when singing up than in logging in?

Rejecting a session during signup will not prevent the RestWrite from creating the user, it will require a bit more refactoring to achieve this, since a user is created before the session.
We would need to roll back and delete the created user.

Rejecting a signup can be done on user triggers, while a session could be used to track logins and reject them for existing users, and maybe later on use them for 2fa capability.

Once a user is signed up (and an account is created _User) there seems to be no point in rejecting a session.

@flovilmart
Copy link
Contributor

But still creating a user is one thing, creating a session is another thing. Either it is fully ignore or it isn’t.

@flovilmart
Copy link
Contributor

Once a user is signed up (and an account is created _User) there seems to be no point in rejecting a session.

Preventing a logIn? Ensuring users have gone through a verification step before actually have credentials etc... lots of reasons actually to prevent session creation. Whicj is exactly the point no?

@georgesjamous
Copy link
Contributor Author

georgesjamous commented Oct 29, 2018

So you think both should be independent ?

  • Rejecting a session during signup should not rollback and delete the user,
    just prevent the session from being created. Let the user be created normally.
  • Rejecting a session during login should prevent the user from being logged in.

@flovilmart
Copy link
Contributor

What do you think?

@flovilmart
Copy link
Contributor

And what are you trying to achieve with the PR, because if it’s simply having hooks in session, I believe there is an ulterior motive

@georgesjamous
Copy link
Contributor Author

I think its good, i will look into it.

Sent with GitHawk

@georgesjamous
Copy link
Contributor Author

Yea, just that i need to cancel some logins easily. There are other ways though, but a session hook is the most direct way.

Sent with GitHawk

@georgesjamous
Copy link
Contributor Author

How about something like this ?
session hooks should now be following the workflow you suggested.

The only error that gets ignored is in beforeDelete.
On signup, the user will be created normally just without a session token.

@flovilmart
Copy link
Contributor

Sorry for the late response! Sounds good then.

If errors are thrown in session’s before Delete, then we should probably indicate by a log that it will continue and proceed

Copy link
Contributor

@flovilmart flovilmart left a comment

Choose a reason for hiding this comment

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

Overall we’re very close. Just a few nits and we’ll be great!

spec/CloudCodeSessionTrigger.spec.js Outdated Show resolved Hide resolved
user.setUsername('some-user-name');
user.setPassword('password');
await user.signUp();
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This try / catch is not necessary

spec/CloudCodeSessionTrigger.spec.js Outdated Show resolved Hide resolved
user.setUsername('user-name');
user.setPassword('user-password');
await user.signUp();
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Unnecessary

spec/CloudCodeSessionTrigger.spec.js Outdated Show resolved Hide resolved
Parse.Cloud.afterDelete('_Session', function() {
const someObject = new Parse.Object('Test');
someObject.set('key', 'value');
someObject.save();
Copy link
Contributor

Choose a reason for hiding this comment

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

You can also use a simple spy or counter to ensure the function has been called. This makes the test suite faster :)

@@ -387,6 +394,11 @@ function mockShortLivedAuth() {
return auth;
}

// delay
function delay(millis) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this function is declared at many places already, do you plan to reuse it everywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

will check if i can consolidate them all here.

error: function (error) {
// ignore errors thrown in before-delete (during logout for example)
if (isSessionTrigger && request.triggerName === Types.beforeDelete) {
return resolve(undefined, error);
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure resolve supports two arguments as it is a native promise IIRC.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Resolve here is a custom function, defined here. Its not the the promise resolve.

(object, error) => {

src/triggers.js Outdated
@@ -524,9 +552,15 @@ export function maybeRunTrigger(
config,
context
);
var { success, error } = getResponseObject(
var {
Copy link
Contributor

Choose a reason for hiding this comment

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

If this line has changed, let’s replace var by const

src/triggers.js Outdated
@@ -582,7 +616,9 @@ export function maybeRunTrigger(
// Converts a REST-format object to a Parse.Object
// data is either className or an object
export function inflate(data, restObject) {
var copy = typeof data == 'object' ? data : { className: data };
var copy = typeof data == 'object' ? data : {
Copy link
Contributor

Choose a reason for hiding this comment

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

Use const if we change this line as well

@georgesjamous
Copy link
Contributor Author

Will look into the requested changes as soon as i can.

@georgesjamous
Copy link
Contributor Author

georgesjamous commented Nov 20, 2018

@flovilmart i think I got them all.
Not sure about the space changes between my commits though.
Although, I am using two different machines but same VSC config.

@stale
Copy link

stale bot commented Mar 2, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants