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

Revert "Feature/eppn email 956" #158

Merged
merged 1 commit into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/backpack/serializers_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ class V1InstanceSerializer(serializers.Serializer):
id = serializers.URLField(required=False)
type = serializers.CharField()
uid = BadgeStringField(required=False)
recipient = BadgeEmailField()
recipient = BadgeEmailField() # TODO: improve for richer types
badge = V1BadgeClassSerializer()
issuedOn = BadgeDateTimeField(required=False) # missing in some translated v0.5.0
expires = BadgeDateTimeField(required=False)
Expand Down
22 changes: 13 additions & 9 deletions apps/badgeuser/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import datetime
import re
from itertools import chain
from django.db.models import Q

from allauth.account.models import EmailAddress, EmailConfirmation
from django.conf import settings
from django.contrib.auth.models import AbstractUser
Expand All @@ -23,7 +23,7 @@
from badgeuser.utils import generate_badgr_username
from cachemodel.decorators import cached_method
from cachemodel.models import CacheModel
from directaward.models import DirectAward, DirectAwardBundle
from directaward.models import DirectAward
from entity.models import BaseVersionedEntity
from issuer.models import BadgeInstance
from lti_edu.models import StudentsEnrolled
Expand Down Expand Up @@ -618,10 +618,8 @@ def schac_homes(self):

@property
def direct_awards(self):
eppn_query = Q(eppn__in=self.eppns)
email_query = Q(recipient_email=self.email, bundle__identifier_type=DirectAwardBundle.IDENTIFIER_EMAIL)
unaccepted_direct_awards = DirectAward.objects.filter(eppn_query | email_query, status='Unaccepted')
return unaccepted_direct_awards
# TODO - add or query to use personal email address
return DirectAward.objects.filter(eppn__in=self.eppns, status='Unaccepted')

def match_provisionments(self):
"""Used to match provisions on initial login"""
Expand All @@ -634,9 +632,15 @@ def email_user(self, subject, html_message):
"""
Sends an email to this User.
"""
# Allow sending, as this email is not blacklisted.
plain_text = strip_tags(html_message)
send_mail(subject, message=plain_text, html_message=html_message, recipient_list=[self.primary_email])
try:
EmailBlacklist.objects.get(email=self.primary_email)
except EmailBlacklist.DoesNotExist:
# Allow sending, as this email is not blacklisted.
plain_text = strip_tags(html_message)
send_mail(subject, message=plain_text, html_message=html_message, recipient_list=[self.primary_email])
else:
return
# TODO: Report email non-delivery somewhere.

def publish(self):
super(BadgeUser, self).publish()
Expand Down
29 changes: 14 additions & 15 deletions apps/badgrsocialauth/providers/eduid/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,18 +216,17 @@ def after_terms_agreement(request, **kwargs):
eppn_json = response.json()
request.user.clear_affiliations()
for info in eppn_json:
if "eppn" in info and "schac_home_organization" in info:
request.user.add_affiliations(
[
{
"eppn": info["eppn"].lower(),
"schac_home": info["schac_home_organization"],
}
]
)
logger.info(
f"Stored affiliations {info['eppn']} {info['schac_home_organization']}"
)
request.user.add_affiliations(
[
{
"eppn": info["eppn"].lower(),
"schac_home": info["schac_home_organization"],
}
]
)
logger.info(
f"Stored affiliations {info['eppn']} {info['schac_home_organization']}"
)
validated_names = [
info["validated_name"] for info in eppn_json if "validated_name" in info
]
Expand All @@ -252,7 +251,7 @@ def after_terms_agreement(request, **kwargs):
request.user.save()

if not social_account:
# This fails if there is already a User account with that email, so we need to delete the old one
# This fails if there is already an User account with that email, so we need to delete the old one
try:
user = (
BadgeUser.objects.filter(is_teacher=False, email=payload["email"])
Expand All @@ -263,8 +262,8 @@ def after_terms_agreement(request, **kwargs):
user.delete()
except BadgeUser.DoesNotExist:
pass

# We don't create welcome badges anymore
# Create an eduIDBadge
create_edu_id_badge_instance(login)

return ret

Expand Down
10 changes: 5 additions & 5 deletions apps/directaward/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,13 @@ def convert_direct_award(direct_award):
}

def convert_badge_assertion(badge_instance):
user = badge_instance.user
return {
"full_name": user.full_name,
"email": user.email,
"full_name": badge_instance.user.full_name,
"email": badge_instance.user.email,
"public": badge_instance.public,
"revoked": badge_instance.revoked,
"entity_id": badge_instance.entity_id,
"eppn": user.eppns[0] if user.eppns else []
"eppn": badge_instance.user.eppns[0]
}

results = {
Expand All @@ -70,7 +69,8 @@ def convert_badge_assertion(badge_instance):
convert_direct_award(da) for da in award_bundle.directaward_set.all()
],
"badge_assertions": [
convert_badge_assertion(ba) for ba in award_bundle.badgeinstance_set.all()
convert_badge_assertion(ba)
for ba in award_bundle.badgeinstance_set.all()
],
}

Expand Down
23 changes: 0 additions & 23 deletions apps/directaward/migrations/0018_auto_20241104_1117.py

This file was deleted.

53 changes: 30 additions & 23 deletions apps/directaward/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

class DirectAward(BaseAuditedModel, BaseVersionedEntity, CacheModel):
recipient_email = models.EmailField()
eppn = models.CharField(max_length=254, blank=True, null=True, default=None)
eppn = models.CharField(max_length=254)
badgeclass = models.ForeignKey("issuer.BadgeClass", on_delete=models.CASCADE)
bundle = models.ForeignKey(
"directaward.DirectAwardBundle", null=True, on_delete=models.CASCADE
Expand Down Expand Up @@ -56,12 +56,11 @@ class DirectAward(BaseAuditedModel, BaseVersionedEntity, CacheModel):

def validate_unique(self, exclude=None):
if (
self.__class__.objects.filter(
eppn=self.eppn, badgeclass=self.badgeclass, status="Unaccepted",
bundle__identifier_type=DirectAwardBundle.IDENTIFIER_EPPN
)
.exclude(pk=self.pk)
.exists()
self.__class__.objects.filter(
eppn=self.eppn, badgeclass=self.badgeclass, status="Unaccepted"
)
.exclude(pk=self.pk)
.exists()
):
raise IntegrityError(
"DirectAward with this eppn and status Unaccepted already exists in the same badgeclass."
Expand All @@ -85,12 +84,29 @@ def award(self, recipient):
"""Accept the direct award and make an assertion out of it"""
from issuer.models import BadgeInstance

if self.eppn not in recipient.eppns and self.recipient_email != recipient.email and self.bundle.identifier_type != DirectAwardBundle.IDENTIFIER_EMAIL:
raise BadgrValidationError("Cannot award, eppn / email does not match", 999)

if not recipient.validated_name:
if self.eppn not in recipient.eppns:
raise BadgrValidationError("Cannot award, eppn does not match", 999)

institution = self.badgeclass.institution
identifiers = [
inst.identifier for inst in self.badgeclass.award_allowed_institutions.all()
] + [institution.identifier]
if institution.alternative_identifier:
identifiers.append(institution.alternative_identifier)
schac_homes = recipient.schac_homes
if self.badgeclass.formal:
allowed = (
institution.identifier in schac_homes
or institution.alternative_identifier in schac_homes
)
else:
allowed = (
any(identifier in schac_homes for identifier in identifiers)
or recipient.validated_name
)
if not allowed:
raise BadgrValidationError(
"Cannot award, you do not have a validated name",
"Cannot award, you are not a member of the institution of the badgeclass",
999,
)
evidence = None
Expand Down Expand Up @@ -162,15 +178,6 @@ class DirectAwardBundle(BaseAuditedModel, BaseVersionedEntity, CacheModel):
status = models.CharField(
max_length=254, choices=STATUS_CHOICES, default=STATUS_ACTIVE
)
IDENTIFIER_EPPN = "eppn"
IDENTIFIER_EMAIL = "email"
IDENTIFIER_TYPES = (
(IDENTIFIER_EPPN, "eppn"),
(IDENTIFIER_EMAIL, "email"),
)
identifier_type = models.CharField(
max_length=254, choices=IDENTIFIER_TYPES, default=IDENTIFIER_EPPN
)
scheduled_at = models.DateTimeField(blank=True, null=True, default=None)

@property
Expand Down Expand Up @@ -205,8 +212,8 @@ def direct_award_revoked_count(self):
direct_award_bundle=self, revoked=True
).count()
return (
revoked_count
+ DirectAward.objects.filter(bundle=self, status="Revoked").count()
revoked_count
+ DirectAward.objects.filter(bundle=self, status="Revoked").count()
)

@property
Expand Down
5 changes: 1 addition & 4 deletions apps/directaward/permissions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,4 @@
class IsDirectAwardOwner(permissions.BasePermission):

def has_object_permission(self, request, view, obj):
from directaward.models import DirectAwardBundle
user = request.user
return obj.eppn in user.eppns or (
obj.recipient_email == user.email and obj.bundle.identifier_type == DirectAwardBundle.IDENTIFIER_EMAIL)
return obj.eppn in request.user.eppns
5 changes: 2 additions & 3 deletions apps/directaward/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Meta:
class DirectAwardBundleType(DjangoObjectType):
class Meta:
model = DirectAwardBundle
fields = ('entity_id', 'badgeclass', 'created_at', 'updated_at', 'identifier_type',
fields = ('entity_id', 'badgeclass', 'created_at', 'updated_at',
'assertion_count', 'direct_award_count', 'direct_award_rejected_count',
'direct_award_scheduled_count', 'direct_award_revoked_count', 'initial_total')

Expand Down Expand Up @@ -43,8 +43,7 @@ def resolve_direct_award(self, info, **kwargs):
id = kwargs.get('id')
if id is not None:
da = DirectAward.objects.get(entity_id=id)
user = info.context.user
if da.eppn in user.eppns or (da.recipient_email == user.email and da.bundle.identifier_type == DirectAwardBundle.IDENTIFIER_EMAIL):
if da.eppn in info.context.user.eppns:
return da

def resolve_all_unclaimed_direct_awards(self, info, **kwargs):
Expand Down
13 changes: 3 additions & 10 deletions apps/directaward/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Meta:
model = DirectAward

badgeclass = BadgeClassSlugRelatedField(slug_field="entity_id", required=False)
eppn = serializers.CharField(required=False, allow_blank=True, allow_null=True)
eppn = serializers.CharField(required=False)
recipient_email = serializers.EmailField(required=False)
status = serializers.CharField(required=False)
evidence_url = serializers.URLField(
Expand All @@ -33,9 +33,7 @@ class Meta:

def validate_eppn(self, eppn):
eppn_reg_exp_format = self.context['request'].user.institution.eppn_reg_exp_format
# For email identifier_type we don't validate eppn
eppn_required = self.root.initial_data["identifier_type"] == "eppn"
if eppn_reg_exp_format and eppn_required:
if eppn_reg_exp_format:
eppn_re = re.compile(eppn_reg_exp_format, re.IGNORECASE)
if not bool(eppn_re.match(eppn)):
raise ValidationError(message="Incorrect eppn format", code="error")
Expand Down Expand Up @@ -70,9 +68,6 @@ class Meta:
status = serializers.CharField(
write_only=True, default="Active", required=False, allow_null=True
)
identifier_type = serializers.CharField(
write_only=True, default="eppn", allow_null=False
)
scheduled_at = serializers.DateTimeField(
write_only=True, required=False, allow_null=True
)
Expand Down Expand Up @@ -101,10 +96,8 @@ def create(self, validated_data):
direct_award_bundle = DirectAwardBundle.objects.create(
initial_total=direct_awards.__len__(), **validated_data
)
eppn_required = validated_data.get("identifier_type", "eppn") == "eppn"
for direct_award in direct_awards:
# Not required and already validated
direct_award["eppn"] = direct_award["eppn"].lower() if eppn_required else None
direct_award["eppn"] = direct_award["eppn"].lower()
status = (
DirectAward.STATUS_SCHEDULED
if scheduled_at
Expand Down
10 changes: 5 additions & 5 deletions apps/entity/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@


def get_form_error_code(error_type):
if error_type == 'null':
if error_type is 'null':
return 901
elif error_type == 'invalid':
elif error_type is 'invalid':
return 902
elif error_type == 'blank':
elif error_type is 'blank':
return 903
elif error_type == 'required':
elif error_type is 'required':
return 904
elif isinstance(error_type, int):
return error_type
Expand All @@ -30,7 +30,7 @@ def validate_errors(serializer):
'error_code': get_form_error_code(vars(error)['code']),
'error_message': error
})
except TypeError:
except TypeError as e: # TODO: make this recursive for endless depth
sub_fields = {}
for sub_attr, sub_errors in error.items():
sub_fields[sub_attr] = []
Expand Down
2 changes: 2 additions & 0 deletions apps/health/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
"""
from django.db import connection, DatabaseError
from django.http import JsonResponse
# import logger # TODO integrate logging into results of health endpoint queries
# import requests # use for making requests to any dependency HTTP APIs.
from rest_framework import status

OK = 'OK'
Expand Down
2 changes: 2 additions & 0 deletions apps/issuer/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@


class DjangoCacheDict(MutableMapping):
"""TODO: Fix this class, its broken!"""
_keymap_cache_key = "DjangoCacheDict_keys"

def __init__(self, namespace, id=None, timeout=None):
Expand Down Expand Up @@ -123,6 +124,7 @@ def translate_errors(cls, badgecheck_messages):
@classmethod
def cache_instance(cls):
if cls._cache_instance is None:
# TODO: note this class is broken and does not work correctly!
cls._cache_instance = DjangoCacheRequestsCacheBackend(namespace='badgr_requests_cache')
return cls._cache_instance

Expand Down
11 changes: 11 additions & 0 deletions apps/issuer/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,17 @@ def save(self, *args, **kwargs):
content=ContentFile(new_image.read()),
save=False)

# TODO can this be permanently removed
# try:
# from badgeuser.models import CachedEmailAddress
# email_address = self.get_email_address()
# existing_email = CachedEmailAddress.cached.get_student_email(email_address)
# if email_address != existing_email.email and \
# email_address not in [e.email for e in existing_email.cached_variants()]:
# existing_email.add_variant(email_address)
# except CachedEmailAddress.DoesNotExist:
# pass

if self.revoked is False:
self.revocation_reason = None

Expand Down
Loading
Loading