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

Feat: add Terms of Use accept view and middleware #1217

Open
wants to merge 8 commits into
base: 2.3.0
Choose a base branch
from

Conversation

MyPyDavid
Copy link
Member

@MyPyDavid MyPyDavid commented Dec 18, 2024

Description

Related issue: #141, #161

It adds a migration for the ConsentFieldValue model: accounts.0022_add_created_updated_to_consent
So it requires a python manage.py migrate.
I have added some methods that handle the consent and session to the model.

configuration in the rdmo-app

When ACCOUNT_TERMS_OF_USE is enabled, some settings need to be configured in the rdmo-app as well.

In the settings from local.py:

if ACCOUNT_TERMS_OF_USE:
    MIDDLEWARE += [
        'rdmo.accounts.middleware.TermsAndConditionsRedirectMiddleware'
    ]

An optional feature is added that sets the date on which the ToU text is valid, so that it can be easily updated when the text might change. The optional setting is TERMS_VERSION_DATE and needs to be a valid date string that is compatible with formats from django get_format('DATE_INPUT_FORMATS'). The date of this setting checks if the user has updated their consent on or after that date, otherwise the consent is invalid and asked to accept again.

# for example
TERMS_VERSION_DATE = "2025-12-30"

Optionally, in the urls.py:

if settings.ACCOUNT_TERMS_OF_USE:
    from rdmo.accounts.views import terms_of_use, terms_of_use_accept
    urlpatterns += [
        path("account/terms-of-use/accept/", terms_of_use_accept, name="terms_of_use_accept"),
        path("account/terms-of-use/", terms_of_use, name="terms_of_use"),
    ]

Motivation and Context

How has this been tested?

Screenshots (if appropriate)

@MyPyDavid MyPyDavid self-assigned this Dec 18, 2024
@MyPyDavid MyPyDavid added this to the RDMO 2.3.0 milestone Dec 18, 2024
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid MyPyDavid force-pushed the feat-add-terms-of-use-middleware branch from bcbd456 to e25b960 Compare January 23, 2025 16:14
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid MyPyDavid force-pushed the feat-add-terms-of-use-middleware branch from e25b960 to 3c2a3bc Compare January 23, 2025 16:16
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
@MyPyDavid
Copy link
Member Author

can be reviewed when tests pass

@MyPyDavid MyPyDavid marked this pull request as ready for review February 4, 2025 08:20
@MyPyDavid MyPyDavid requested a review from jochenklar February 4, 2025 08:20
@MyPyDavid MyPyDavid changed the title Feat: add Terms of Use update view and middleware Feat: add Terms of Use accept view and middleware Feb 4, 2025

def is_consent_valid(self) -> bool:
# optionally enable terms to be outdated
terms_version_date = getattr(settings, 'TERMS_VERSION_DATE', None)
Copy link
Member Author

Choose a reason for hiding this comment

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

As a flow for invalidating the consent (eg. when the text needs an update or the terms changed),
I thought it would be helpful to be able to simply set this date as a setting instead of manually deleting all of the ConsentFieldValue objects.
Is the name of the TERMS_VERSION_DATE ok? Should it be added to the normal core/settings as TERMS_VERSION_DATE = None ?

Copy link
Member

Choose a reason for hiding this comment

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

I like it! And (see my comment above) should have a sensible default (None) in rdmo.core.settings.

Copy link
Member

Choose a reason for hiding this comment

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

Line 166 can be removed.

Copy link
Member

Choose a reason for hiding this comment

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

Actually I would call it ACCOUNT_TERMS_OF_USE_DATE to have some kind of ACCOUNT_TERMS_OF_USE_ namespacing in the settings.

Copy link
Member

Choose a reason for hiding this comment

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

Same for the other terms of use related settings.

Copy link
Member

Choose a reason for hiding this comment

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

I would stick to having a default value for the settings used in RDMO (otherwise we are inconsistent in rdmo), but something like this could be nice for plugins.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, I mean in addition to setting the default. The default is None and then when another value is set, it needs to be a valid to-a-date-parse-able string.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I would not start checking settings. If RDMO is misconfigured exceptions will happen. If you mean to check validate the settings once when starting and outside of the usage of the settings in the code, I am fine with it. But I don't want to have validate(settings.FOO_BAR) everywhere.

Copy link
Member Author

Choose a reason for hiding this comment

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

yes, it's exactly that, for manage.py check:

SystemCheckError: System check identified some issues:

ERRORS:
?: (core.E001) ACCOUNT_TERMS_OF_USE_DATE = 2025-02-36 is not a valid date string.
        HINT: day is out of range for month

System check identified 1 issue (0 silenced).
❯

Copy link
Member

Choose a reason for hiding this comment

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

Awesome, go for it.

Copy link
Member

@jochenklar jochenklar left a comment

Choose a reason for hiding this comment

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

Great work! I think I will need test it some more with ORCID etc. when we have a release candidate, but I think this approach will work for all.


from .models import ConsentFieldValue

# these exclude url settings are optional
Copy link
Member

Choose a reason for hiding this comment

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

My philosophy in RDMO was to add all settings to rdmo.core.settings in order to avoid those "checking" blocks. (getattr is already better than try ... execpt.) I suggest we stick to this.

if (
settings.ACCOUNT_TERMS_OF_USE # Terms enforcement enabled
and request.user.is_authenticated
and request.path != reverse("terms_of_use_accept")
Copy link
Member

Choose a reason for hiding this comment

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

Could be part of is_path_protected.


obj.delete() # Remove when consent is outdated
return False

Copy link
Member

Choose a reason for hiding this comment

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

Remove newline.

return False


@classmethod
Copy link
Member

Choose a reason for hiding this comment

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

I actually likes it better when this was in utils. (I just forgot to pull and looked at an older version of the branch.)

Copy link
Member

Choose a reason for hiding this comment

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

Ok after looking at it some more, it makes sense together with the other methods.

Copy link
Member Author

Choose a reason for hiding this comment

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

I used the "Fat Models" approach for this and put everything relevant on the model..


def is_consent_valid(self) -> bool:
# optionally enable terms to be outdated
terms_version_date = getattr(settings, 'TERMS_VERSION_DATE', None)
Copy link
Member

Choose a reason for hiding this comment

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

I like it! And (see my comment above) should have a sensible default (None) in rdmo.core.settings.


def is_consent_valid(self) -> bool:
# optionally enable terms to be outdated
terms_version_date = getattr(settings, 'TERMS_VERSION_DATE', None)
Copy link
Member

Choose a reason for hiding this comment

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

Line 166 can be removed.

# First, try standard ISO format (YYYY-MM-DD)
latest_terms_version_date = parse_date(terms_version_date)

# If ISO parsing fails, try localized formats
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if we already use a more generic datetime parsing function in RDMO. If not we should put something like this in rdmo.core.utils. Maybe using datetime.fromisoformat(string) would suffice.

Copy link
Member

Choose a reason for hiding this comment

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

Ah I see that parse_date is already the django version of this.

from django.utils.translation import gettext_lazy as _

from rdmo.core.models import Model as RDMOTimeStampedModel
Copy link
Member

Choose a reason for hiding this comment

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

I am not a fan of renaming in import, but I guess naming it just Model was not super clever back then.


def is_consent_valid(self) -> bool:
# optionally enable terms to be outdated
terms_version_date = getattr(settings, 'TERMS_VERSION_DATE', None)
Copy link
Member

Choose a reason for hiding this comment

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

Actually I would call it ACCOUNT_TERMS_OF_USE_DATE to have some kind of ACCOUNT_TERMS_OF_USE_ namespacing in the settings.


def is_consent_valid(self) -> bool:
# optionally enable terms to be outdated
terms_version_date = getattr(settings, 'TERMS_VERSION_DATE', None)
Copy link
Member

Choose a reason for hiding this comment

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

Same for the other terms of use related settings.

@MyPyDavid
Copy link
Member Author

Great work! I think I will need test it some more with ORCID etc. when we have a release candidate, but I think this approach will work for all.

I think what is still missing is the integration with the social signup form, like the account sign up already has.

…d add checks

Signed-off-by: David Wallace <david.wallace@tu-darmstadt.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Social login does not show Terms of Use No Terms of Use when using Shibboleth
2 participants