forked from City-of-Turku/tunnistamo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
scopes.py
108 lines (88 loc) · 3.49 KB
/
scopes.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
from django.utils.translation import ugettext_lazy as _
from oidc_provider.lib.claims import ScopeClaims, StandardScopeClaims
from .models import ApiScope
from .utils import combine_uniquely
class ApiScopeClaims(ScopeClaims):
@classmethod
def get_scopes_info(cls, scopes=[]):
scopes_by_identifier = {
api_scope.identifier: api_scope
for api_scope in ApiScope.objects.by_identifiers(scopes)
}
api_scopes = (scopes_by_identifier.get(scope) for scope in scopes)
return [
{
'scope': api_scope.identifier,
'name': api_scope.name,
'description': api_scope.description,
}
for api_scope in api_scopes if api_scope
]
class GithubUsernameScopeClaims(ScopeClaims):
info_github_username = (
_("GitHub username"), _("Access to your GitHub username."))
def scope_github_username(self):
social_accounts = self.user.socialaccount_set
github_account = social_accounts.filter(provider='github').first()
if not github_account:
return {}
github_data = github_account.extra_data
return {
'github_username': github_data.get('login'),
}
class CombinedScopeClaims(ScopeClaims):
combined_scope_claims = [
StandardScopeClaims,
GithubUsernameScopeClaims,
ApiScopeClaims,
]
@classmethod
def get_scopes_info(cls, scopes=[]):
extended_scopes = cls._extend_scope(scopes)
scopes_info_map = {}
for claim_cls in cls.combined_scope_claims:
for info in claim_cls.get_scopes_info(extended_scopes):
scopes_info_map[info['scope']] = info
return [
scopes_info_map[scope]
for scope in extended_scopes
if scope in scopes_info_map
]
@classmethod
def _extend_scope(cls, scopes):
required_scopes = cls._get_all_required_scopes_by_api_scopes(scopes)
extended_scopes = combine_uniquely(scopes, sorted(required_scopes))
return extended_scopes
@classmethod
def _get_all_required_scopes_by_api_scopes(cls, scopes):
api_scopes = ApiScope.objects.by_identifiers(scopes)
apis = {x.api for x in api_scopes}
return set(sum((list(api.required_scopes) for api in apis), []))
def create_response_dic(self):
result = super(CombinedScopeClaims, self).create_response_dic()
token = FakeToken.from_claims(self)
for claim_cls in self.combined_scope_claims:
claim = claim_cls(token)
result.update(claim.create_response_dic())
return result
class FakeToken(object):
"""
Object that adapts a token.
ScopeClaims constructor needs a token, but really uses just its
user, scope and client attributes. This adapter makes it possible
to create a token like object from those three attributes or from a
claims object (which doesn't store the token) allowing it to be
passed to a ScopeClaims constructor.
"""
def __init__(self, user, scope, client):
self.user = user
self.scope = scope
self.client = client
@classmethod
def from_claims(cls, claims):
return cls(claims.user, claims.scopes, claims.client)
def get_userinfo_by_scopes(user, scopes, client=None):
token = FakeToken(user, scopes, client)
return _get_userinfo_by_token(token)
def _get_userinfo_by_token(token):
return CombinedScopeClaims(token).create_response_dic()