IEEE.org     |     IEEE Xplore Digital Library     |     IEEE Standards     |     IEEE Spectrum     |     More Sites

Verified Commit ca2d9e67 authored by Emi Simpson's avatar Emi Simpson
Browse files

[api] Add missing file that was forgotten in a previous commit

parent e9f71bc4
Pipeline #1113 passed with stage
in 51 seconds
from dataclasses import dataclass
from flask_saml2.sp.sp import ServiceProvider #type: ignore
from functools import wraps
from typing import Callable, Concatenate, Mapping, Optional, ParamSpec, TypeVar
from mystic.types import Url, UserID
@dataclass(frozen=True)
class DictMalformed:
pass
@dataclass(frozen=True)
class AuthAttrs:
uidNumber: UserID
"""
The user's university ID (9-digits)
"""
uid: str
"""
The user's RIT username (e.g. xyz1234)
"""
givenName: str
"""
The user's first name (updates from SIS's prefered name field)
"""
sn: str
"""
The user's last name
(I didn't name the SAML fields don't blame me for the weird name)
"""
mail: str
"""
One of the user's email addresses
This is the prefered email address, which might be an @rit.edu, @g.rit.edu, or a
@mail.rit.edu
"""
@staticmethod
def from_dict(d: Mapping[str, str]) -> Optional['AuthAttrs']:
"""
Creates an :class:`AuthAttrs` from the attributes returned by RIT's SAML api
A `None` result indicates that one or more fields was missing, or the `uidNumber`
field was malformed
"""
try:
return AuthAttrs(
UserID(int(d['uidNumber'])),
d['uid'],
d['givenName'],
d['sn'],
d['mail'])
except KeyError:
return None
except ValueError:
return None
@staticmethod
def check_logged_in(provider: ServiceProvider) -> 'AuthAttrs | None | DictMalformed':
"""
Pulls the logged in user's information from the SAML data
Returns `None` if the user isn't logged in
Returns `DictMalformed` if SAML data is present, but wasn't in the expected shape
Otherwise, returns the `AuthAttrs` that describe the user
"""
try:
user_info = provider.get_auth_data_in_session().attributes
attrs = AuthAttrs.from_dict(user_info)
if attrs is not None:
# User data exists and was correctly formed
return attrs
else:
# User data exists but was malformed
return DictMalformed()
except KeyError:
# No session data exists
return None
P = ParamSpec('P')
O = TypeVar('O')
def provide_saml_data(provider: ServiceProvider) -> Callable[[Callable[Concatenate['AuthAttrs | None | DictMalformed', Url, P], O]], Callable[P, O]]:
"""
Decorator which accesses information about the currently logged in user from SAML
The first argument passed is whatever information is available about the logged in
user, if any. For further info, please consult :class:`AuthAttrs`.
The second argument represents the URL that the user should be sent to to begin the
external SAML login flow.
"""
def decorator(func: Callable[Concatenate[AuthAttrs | None | DictMalformed, Url, P], O]) -> Callable[P, O]:
@wraps(func)
def wrapped(*args: P.args, **kwargs: P.kwargs) -> O:
return func(AuthAttrs.check_logged_in(provider), Url(provider.get_login_url()), *args, **kwargs)
return wrapped
return decorator
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment