Skip to content

[policies] Add infrastructure to track user decisions to policies #9818

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

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

skarya22
Copy link
Contributor

@skarya22 skarya22 commented May 28, 2025

Brief summary of changes

  • Added policies table for projects to implement policies that users must agree to to use certain modules or to request a LORIS account.
  • Configurable so that you can choose the following:
    • Which module it should be for
    • Should it be a accessible by a button in the header
    • Should the user be forced to accept the policy before being able to access this module?
    • How long in between accepting the policy do they have to accept it again?
If there is a policy for the login module
image image image
If there is a policy for a non-anonymous module, and HeaderButton = 'Y'
image

Testing instructions (if applicable)

  1. Two policies have been added to raisinbread, confirm they are present SELECT * FROM POLICIES
  2. Open the Dataquery module, you should be prompted to accept the terms of use
  3. Select Decline, and you should be sent to the LORIS homepage
  4. Confirm the decision was saved for your user select * from user_policy_decision
  5. Open the module again and press Accept, you should be able to access the module now. Confirm your decision was saved.
  6. Exit the module, and re-open it, confirm that it does not prompt you for a policy again.
  7. Modify user_policy_decision and change it to that you accepted the policy 8 days from today
  8. Re-open the module and see that it prompts you to accept the terms of use again
  9. Log out and go to the Request Account page
  10. Enter all the details but do not press Terms of Use
  11. Try to submit and see that it makes you view the terms of use first
  12. View the terms of use and try again
  13. Try to create your own policy for a module and confirm that it behaves how you expect

Link(s) to related issue(s)

  • No issue, but both CCNA and EEGnet had their own implementations, this will reduce the number of overrides

@github-actions github-actions bot added Language: SQL PR or issue that update SQL code RaisinBread PR or issue introducing/requiring improvements to the Raidinbread dataset Language: PHP PR or issue that update PHP code Language: Javascript PR or issue that update Javascript code Module: login PR or issue related to login module labels May 28, 2025
@skarya22 skarya22 added Project: CCNA Issue or PR related to the CCNA project Project: EEGNet Issue or PR related to the EEGNet project labels May 28, 2025
Copy link
Collaborator

@driusan driusan left a comment

Choose a reason for hiding this comment

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

I like the idea of this and did a first pass at skimming it. I think the structure should be reworked.

In addition to the changes requested, on a high level I think it would make sense to have a "Policy" class to group the behaviour together rather than everything being ad-hoc strings and Utility functions and htdocs files.

exit(0);
}

\Utility::saveUserPolicyDecision(
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't know why this would go in the Utility class.. is there any circumstance other than this endpoint where it would be used?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yup! Since the policy table can be used to track any User decision, we have a use-case in CCNA where we have a button on a specific page, and whenever a user presses it one of our projects wanted to keep track of that - so we need this function in a place where other projects can use it without having to rewrite it

@@ -11,6 +11,7 @@ import {
CheckboxElement,
ButtonElement,
} from 'jsx/Form';
import {PolicyButton} from '../../../jsx/PolicyButton';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this need to be done every place that a policy might be added?

If so, I think the dataquery module is also very common, and it's probably more common to require the ToU on login than on request account?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case it could be added to the dashboard module instead in the policies table and then when they login it pops up

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To clarify on this further, the import needs to be done whenever we want a button to open the policy, rather than having it pop-up when the module is opened, or have a button in the Header. Auto pop-up and a header button do not require the PolicyButton, but since on the login page we do not have a header, and do not want it to pop-up immediately on opening LORIS, we need a specialized button. I added it as a jsx component rather than to login module directly because I can imagine a use case where a project may want to have the ability to open the policy again on-click of a button in another module

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 was about to remove the request account Terms of Use feature which is the cause for needing it in htdocs and the PolicyButton.jsx files, but remembered that the entire use case of this for EEGNET is on request account. Can view it at eegnet.loris.ca

* @return array The policy details
* @throws \DatabaseException
*/
static function getPolicy(
Copy link
Collaborator

Choose a reason for hiding this comment

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

This doesn't really belong in Utility. From what I can tell, policies are per module (based on this function signature), so I think it would make sense to have a non-static module method. (It's not clear if policies are enforced per-page or per module, if per page maybe it should be in NDB_Page)

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 can move it to NDB_Page probably

bool $headerButton = false
) {
$factory = \NDB_Factory::singleton();
$DB = $factory->database();
Copy link
Collaborator

Choose a reason for hiding this comment

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

database connections should come from the LorisInstance object

$DB = $factory->database();
// If no policy name is given, we return the latest policy for the module
if (!empty($policyName)) {
$policyName = 'AND p.Name = '. $DB->quote($policyName);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't really understand why some variables are concatenated and some are using prepared statements.

(Prepared statement variables are the better option..)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I put $policyName as a prepared statement variable then it would not work when no policyName is provided, right?

SET FOREIGN_KEY_CHECKS=0;
TRUNCATE TABLE `policies`;
LOCK TABLES `policies` WRITE;
INSERT INTO `policies` (`PolicyID`, `Name`, `Version`, `ModuleID`, `PolicyRenewalTime`, `PolicyRenewalTimeUnit`, `Content`, `SwalTitle`, `HeaderButton`, `HeaderButtonText`, `Active`, `AcceptButtonText`, `DeclineButtonText`, `CreatedAt`, `UpdatedAt`) VALUES (1,'dataquery_example','v1',49,7,'D','By using this Data Query Tool, you acknowledge that you know it is in beta and may not work as expected. You also agree to use it responsibly and not to misuse the data.','Terms Of Use','Y','Terms Of Use','Y','Yes, I accept','Decline','2025-05-28 10:54:21','2025-05-28 10:54:21');
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm confused by the fact that the example in the database is for the data query tool but the code being changed is for request account

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There are two example policies. The one for the login page is right under this.

The one for dataquery does not require any edits to the module as it is inherited from NDB_Page and will work the same for all pages / modules. So any module just needs to have an entry in policies for it to show up.

I had to make changes to request account to enforce that if there is a policy there it is enforced where you have to view it to request an account

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To clarify this a bit further as well, as mentioned there are two example policies in this PR.

  1. A pop-up policy for the DQT that both pop-up when the module is opened, but is also re-openable whenever you press on Terms of Use in the header while the module is open. This behaviour is easily reproducible in any module just through sql.
  2. A policy that is tracked on the login page when a user is requesting an account. It's a different use case than the regular pop-up policies since we need a button in request account rather than having it automatically pop-up or having a button in the header. So for this one we need to manually add the PolicyButton, versus the other implementation where the policy is enforced / added through SQL.

@regisoc
Copy link
Contributor

regisoc commented Jul 7, 2025

@jeffersoncasimir let me know if you have time for this one, else re-assign it to me.

@jeffersoncasimir
Copy link
Contributor

@regisoc I don't think this is ready for review. Not passing tests, no changes since previous review and likely needs rebase

@skarya22 skarya22 added the State: Needs work PR awaiting additional work by the author to proceed label Jul 10, 2025
@skarya22
Copy link
Contributor Author

@jeffersoncasimir sorry about that -- you're right it went off my radar when I went on vacation. I'll fix it up and then request a review! Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Language: Javascript PR or issue that update Javascript code Language: PHP PR or issue that update PHP code Language: SQL PR or issue that update SQL code Module: login PR or issue related to login module Project: CCNA Issue or PR related to the CCNA project Project: EEGNet Issue or PR related to the EEGNet project RaisinBread PR or issue introducing/requiring improvements to the Raidinbread dataset State: Needs work PR awaiting additional work by the author to proceed
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants