Skip to content

Commit 47e2f83

Browse files
committed
Deprecate UserIdHashType
There seems to be no reason for the mapping between the nameid-format and the hash-type. Hashing data for no reason seems to be causing problems, so this behaviour will be removed, starting by deprecating this relation between the nameid-format and the "hash type". Also see: https://lists.sunet.se/pipermail/idpy-discuss/2018-September/000274.html Signed-off-by: Ivan Kanakarakis <[email protected]>
1 parent 082bc2a commit 47e2f83

File tree

12 files changed

+218
-163
lines changed

12 files changed

+218
-163
lines changed

src/satosa/deprecated.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import warnings as _warnings
2+
from enum import Enum
3+
4+
from saml2.saml import NAMEID_FORMAT_TRANSIENT
5+
from saml2.saml import NAMEID_FORMAT_PERSISTENT
6+
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
7+
from saml2.saml import NAMEID_FORMAT_UNSPECIFIED
8+
9+
10+
_warnings.simplefilter("default")
11+
12+
13+
class UserIdHashType(Enum):
14+
"""
15+
All different user id hash types
16+
"""
17+
18+
transient = 1
19+
persistent = 2
20+
pairwise = 3
21+
public = 4
22+
emailaddress = 5
23+
unspecified = 6
24+
25+
def __getattr__(self, name):
26+
msg = "UserIdHashType is deprecated and will be removed."
27+
_warnings.warn(msg, DeprecationWarning)
28+
return self.__getattribute__(name)
29+
30+
@classmethod
31+
def from_string(cls, str):
32+
msg = "UserIdHashType is deprecated and will be removed."
33+
_warnings.warn(msg, DeprecationWarning)
34+
try:
35+
return getattr(cls, str)
36+
except AttributeError:
37+
raise ValueError("Unknown hash type '{}'".format(str))
38+
39+
40+
def saml_name_id_format_to_hash_type(name_format):
41+
"""
42+
Translate pySAML2 name format to satosa format
43+
44+
:type name_format: str
45+
:rtype: satosa.internal_data.UserIdHashType
46+
:param name_format: SAML2 name format
47+
:return: satosa format
48+
"""
49+
msg = "saml_name_id_format_to_hash_type is deprecated and will be removed."
50+
_warnings.warn(msg, DeprecationWarning)
51+
52+
name_id_format_to_hash_type = {
53+
NAMEID_FORMAT_TRANSIENT: UserIdHashType.transient,
54+
NAMEID_FORMAT_PERSISTENT: UserIdHashType.persistent,
55+
NAMEID_FORMAT_EMAILADDRESS: UserIdHashType.emailaddress,
56+
NAMEID_FORMAT_UNSPECIFIED: UserIdHashType.unspecified,
57+
}
58+
59+
return name_id_format_to_hash_type.get(
60+
name_format, UserIdHashType.transient
61+
)
62+
63+
64+
def hash_type_to_saml_name_id_format(hash_type):
65+
"""
66+
Translate satosa format to pySAML2 name format
67+
68+
:type hash_type: satosa.internal_data.UserIdHashType
69+
:rtype: str
70+
:param hash_type: satosa format
71+
:return: pySAML2 name format
72+
"""
73+
msg = "hash_type_to_saml_name_id_format is deprecated and will be removed."
74+
_warnings.warn(msg, DeprecationWarning)
75+
76+
hash_type_to_name_id_format = {
77+
UserIdHashType.transient: NAMEID_FORMAT_TRANSIENT,
78+
UserIdHashType.persistent: NAMEID_FORMAT_PERSISTENT,
79+
UserIdHashType.emailaddress: NAMEID_FORMAT_EMAILADDRESS,
80+
UserIdHashType.unspecified: NAMEID_FORMAT_UNSPECIFIED,
81+
}
82+
83+
return hash_type_to_name_id_format.get(hash_type, NAMEID_FORMAT_PERSISTENT)
84+
85+
86+
def oidc_subject_type_to_hash_type(subject_type):
87+
msg = "oidc_subject_type_to_hash_type is deprecated and will be removed."
88+
_warnings.warn(msg, DeprecationWarning)
89+
90+
if subject_type == "public":
91+
return UserIdHashType.public
92+
93+
return UserIdHashType.pairwise

src/satosa/frontends/openid_connect.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,16 @@
2222

2323
from .base import FrontendModule
2424
from ..internal_data import InternalRequest
25-
from ..internal_data import UserIdHashType
2625
from ..logging_util import satosa_logging
2726
from ..response import BadRequest, Created
2827
from ..response import SeeOther, Response
2928
from ..response import Unauthorized
3029
from ..util import rndstr
3130

32-
logger = logging.getLogger(__name__)
33-
31+
from satosa.deprecated import oidc_subject_type_to_hash_type
3432

35-
def oidc_subject_type_to_hash_type(subject_type):
36-
if subject_type == "public":
37-
return UserIdHashType.public
3833

39-
return UserIdHashType.pairwise
34+
logger = logging.getLogger(__name__)
4035

4136

4237
class OpenIDConnectFrontend(FrontendModule):
@@ -140,12 +135,12 @@ def handle_backend_error(self, exception):
140135
"""
141136
auth_req = self._get_authn_request_from_state(exception.state)
142137
# If the client sent us a state parameter, we should reflect it back according to the spec
143-
if 'state' in auth_req:
138+
if 'state' in auth_req:
144139
error_resp = AuthorizationErrorResponse(error="access_denied",
145140
error_description=exception.message,
146141
state=auth_req['state'])
147-
else:
148-
error_resp = AuthorizationErrorResponse(error="access_denied",
142+
else:
143+
error_resp = AuthorizationErrorResponse(error="access_denied",
149144
error_description=exception.message)
150145
satosa_logging(logger, logging.DEBUG, exception.message, exception.state)
151146
return SeeOther(error_resp.request(auth_req["redirect_uri"], should_fragment_encode(auth_req)))
@@ -286,7 +281,7 @@ def _handle_authn_request(self, context):
286281

287282
client_id = authn_req["client_id"]
288283
context.state[self.name] = {"oidc_request": request}
289-
hash_type = oidc_subject_type_to_hash_type(self.provider.clients[client_id].get("subject_type", "pairwise"))
284+
hash_type = self.provider.clients[client_id].get("subject_type", "pairwise")
290285
client_name = self.provider.clients[client_id].get("client_name")
291286
if client_name:
292287
# TODO should process client names for all languages, see OIDC Registration, Section 2.1

src/satosa/frontends/saml2.py

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -27,57 +27,32 @@
2727
from satosa.base import SAMLBaseModule
2828
from satosa.context import Context
2929
from .base import FrontendModule
30-
from ..internal_data import InternalRequest, UserIdHashType
30+
from ..internal_data import InternalRequest
3131
from ..logging_util import satosa_logging
3232
from ..response import Response
3333
from ..response import ServiceError
3434
from ..saml_util import make_saml_response
3535
import satosa.util as util
3636

37-
38-
logger = logging.getLogger(__name__)
37+
from satosa.deprecated import saml_name_id_format_to_hash_type
38+
from satosa.deprecated import hash_type_to_saml_name_id_format
3939

4040

41-
def saml_name_id_format_to_hash_type(name_format):
42-
"""
43-
Translate pySAML2 name format to satosa format
44-
45-
:type name_format: str
46-
:rtype: satosa.internal_data.UserIdHashType
47-
:param name_format: SAML2 name format
48-
:return: satosa format
49-
"""
50-
name_id_format_to_hash_type = {
51-
NAMEID_FORMAT_TRANSIENT: UserIdHashType.transient,
52-
NAMEID_FORMAT_PERSISTENT: UserIdHashType.persistent,
53-
NAMEID_FORMAT_EMAILADDRESS: UserIdHashType.emailaddress,
54-
NAMEID_FORMAT_UNSPECIFIED: UserIdHashType.unspecified,
55-
}
56-
57-
return name_id_format_to_hash_type.get(
58-
name_format,
59-
UserIdHashType.transient)
41+
logger = logging.getLogger(__name__)
6042

6143

62-
def hash_type_to_saml_name_id_format(hash_type):
63-
"""
64-
Translate satosa format to pySAML2 name format
44+
subject_type_map = {
45+
NAMEID_FORMAT_TRANSIENT: NAMEID_FORMAT_TRANSIENT,
46+
NAMEID_FORMAT_PERSISTENT: NAMEID_FORMAT_PERSISTENT,
47+
NAMEID_FORMAT_EMAILADDRESS: NAMEID_FORMAT_EMAILADDRESS,
48+
NAMEID_FORMAT_UNSPECIFIED: NAMEID_FORMAT_UNSPECIFIED,
49+
"public": NAMEID_FORMAT_PERSISTENT,
50+
"pairwise": NAMEID_FORMAT_TRANSIENT,
51+
}
6552

66-
:type hash_type: satosa.internal_data.UserIdHashType
67-
:rtype: str
68-
:param hash_type: satosa format
69-
:return: pySAML2 name format
70-
"""
71-
hash_type_to_name_id_format = {
72-
UserIdHashType.transient: NAMEID_FORMAT_TRANSIENT,
73-
UserIdHashType.persistent: NAMEID_FORMAT_PERSISTENT,
74-
UserIdHashType.emailaddress: NAMEID_FORMAT_EMAILADDRESS,
75-
UserIdHashType.unspecified: NAMEID_FORMAT_UNSPECIFIED,
76-
}
7753

78-
return hash_type_to_name_id_format.get(
79-
hash_type,
80-
NAMEID_FORMAT_PERSISTENT)
54+
def subject_type_to_saml_nameid_format(subject_type):
55+
return subject_type_map.get(subject_type, NAMEID_FORMAT_PERSISTENT)
8156

8257

8358
class SAMLFrontend(FrontendModule, SAMLBaseModule):
@@ -224,14 +199,14 @@ def _handle_authn_request(self, context, binding_in, idp):
224199
context.request.get("RelayState"))
225200

226201
if authn_req.name_id_policy and authn_req.name_id_policy.format:
227-
name_format = saml_name_id_format_to_hash_type(authn_req.name_id_policy.format)
202+
name_format = authn_req.name_id_policy.format
228203
else:
229204
# default to name id format from metadata, or just transient name id
230205
name_format_from_metadata = idp.metadata[requester]["spsso_descriptor"][0].get("name_id_format")
231206
if name_format_from_metadata:
232-
name_format = saml_name_id_format_to_hash_type(name_format_from_metadata[0]["text"])
207+
name_format = name_format_from_metadata[0]["text"]
233208
else:
234-
name_format = UserIdHashType.transient
209+
name_format = NAMEID_FORMAT_TRANSIENT
235210

236211
requester_name = self._get_sp_display_name(idp, requester)
237212
internal_req = InternalRequest(name_format, requester, requester_name)
@@ -322,11 +297,16 @@ def _handle_authn_response(self, context, internal_response, idp):
322297
for k in attributes_to_remove:
323298
ava.pop(k, None)
324299

325-
name_id = NameID(text=internal_response.user_id,
326-
format=hash_type_to_saml_name_id_format(
327-
internal_response.user_id_hash_type),
328-
sp_name_qualifier=None,
329-
name_qualifier=None)
300+
nameid_value = internal_response.user_id
301+
nameid_format = subject_type_to_saml_nameid_format(
302+
internal_response.user_id_hash_type
303+
)
304+
name_id = NameID(
305+
text=nameid_value,
306+
format=nameid_format,
307+
sp_name_qualifier=None,
308+
name_qualifier=None,
309+
)
330310

331311
dbgmsg = "returning attributes %s" % json.dumps(ava)
332312
satosa_logging(logger, logging.DEBUG, dbgmsg, context.state)

src/satosa/internal_data.py

Lines changed: 20 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,14 @@
33
for converting from SAML/OAuth/OpenID connect to the internal representation.
44
"""
55
import datetime
6-
from enum import Enum
7-
8-
import satosa.util as util
96

7+
from saml2.saml import NAMEID_FORMAT_TRANSIENT
8+
from saml2.saml import NAMEID_FORMAT_PERSISTENT
9+
from saml2.saml import NAMEID_FORMAT_EMAILADDRESS
10+
from saml2.saml import NAMEID_FORMAT_UNSPECIFIED
1011

11-
class UserIdHashType(Enum):
12-
"""
13-
All different user id hash types
14-
"""
15-
transient = 1
16-
persistent = 2
17-
pairwise = 3
18-
public = 4
19-
emailaddress = 5
20-
unspecified = 6
21-
22-
@classmethod
23-
def from_string(cls, str):
24-
try:
25-
return getattr(cls, str)
26-
except AttributeError:
27-
raise ValueError("Unknown hash type '{}'".format(str))
12+
import satosa.util as util
13+
from satosa.deprecated import UserIdHashType
2814

2915

3016
class UserIdHasher(object):
@@ -44,7 +30,7 @@ def save_state(internal_request, state):
4430
:param state: The current state
4531
"""
4632
state_data = {
47-
"hash_type": internal_request.user_id_hash_type.name
33+
"hash_type": internal_request.user_id_hash_type
4834
}
4935
state[UserIdHasher.STATE_KEY] = state_data
5036

@@ -63,7 +49,7 @@ def hash_data(salt, value):
6349
@staticmethod
6450
def hash_type(state):
6551
state_data = state[UserIdHasher.STATE_KEY]
66-
hash_type = UserIdHashType.from_string(state_data["hash_type"])
52+
hash_type = state_data["hash_type"]
6753
return hash_type
6854

6955
@staticmethod
@@ -85,12 +71,12 @@ def hash_id(salt, user_id, requester, state):
8571
:return: the internal_response containing the hashed user ID
8672
"""
8773
hash_type_to_format = {
88-
UserIdHashType.transient: '{id}{req}{time}',
89-
UserIdHashType.persistent: '{id}{req}',
90-
UserIdHashType.pairwise: '{id}{req}',
91-
UserIdHashType.public: '{id}',
92-
UserIdHashType.emailaddress: '{id}',
93-
UserIdHashType.unspecified: '{id}',
74+
NAMEID_FORMAT_TRANSIENT: '{id}{req}{time}',
75+
NAMEID_FORMAT_PERSISTENT: '{id}{req}',
76+
"pairwise": '{id}{req}',
77+
"public": '{id}',
78+
NAMEID_FORMAT_EMAILADDRESS: '{id}',
79+
NAMEID_FORMAT_UNSPECIFIED: '{id}',
9480
}
9581

9682
format_args = {
@@ -110,8 +96,8 @@ def hash_id(salt, user_id, requester, state):
11096
hasher = (
11197
(lambda salt, value: value)
11298
if hash_type in [
113-
UserIdHashType.emailaddress,
114-
UserIdHashType.unspecified,
99+
NAMEID_FORMAT_EMAILADDRESS,
100+
NAMEID_FORMAT_UNSPECIFIED,
115101
]
116102
else UserIdHasher.hash_data)
117103
return hasher(salt, user_id)
@@ -179,7 +165,7 @@ def __init__(self, user_id_hash_type, requester, requester_name=None):
179165
:param user_id_hash_type:
180166
:param requester: identifier of the requester
181167
182-
:type user_id_hash_type: UserIdHashType
168+
:type user_id_hash_type: str
183169
:type requester: str
184170
"""
185171
self.user_id_hash_type = user_id_hash_type
@@ -197,7 +183,7 @@ class InternalResponse(InternalData):
197183
198184
:type _user_id: str
199185
:type attributes: dict[str, str]
200-
:type user_id_hash_type: UserIdHashType
186+
:type user_id_hash_type: str
201187
:type auth_info: AuthenticationInformation
202188
"""
203189

@@ -221,7 +207,7 @@ def from_dict(int_resp_dict):
221207
auth_info = AuthenticationInformation.from_dict(int_resp_dict["auth_info"])
222208
internal_response = InternalResponse(auth_info=auth_info)
223209
if "hash_type" in int_resp_dict:
224-
internal_response.user_id_hash_type = UserIdHashType.from_string(int_resp_dict["hash_type"])
210+
internal_response.user_id_hash_type = int_resp_dict["hash_type"]
225211
internal_response.attributes = int_resp_dict["attr"]
226212
internal_response.user_id = int_resp_dict["usr_id"]
227213
internal_response.requester = int_resp_dict["to"]
@@ -238,5 +224,5 @@ def to_dict(self):
238224
"to": self.requester,
239225
"auth_info": self.auth_info.to_dict()}
240226
if self.user_id_hash_type:
241-
_dict["hash_type"] = self.user_id_hash_type.name
227+
_dict["hash_type"] = self.user_id_hash_type
242228
return _dict

0 commit comments

Comments
 (0)