def _export_own_metadata(self): """recompute our own metadata.""" from dm.saml2.pyxb import metadata ed = metadata.EntityDescriptor( entityID=self.entity_id, validUntil=utcnow() + self.metadata_validity, ) ld = metadata.__dict__ for r, p in self.roles.items(): if r == "ap": continue # for the moment i = self.unrestrictedTraverse(p) rd = ld[role2element[r]]() getattr(ed, rd.__class__.__name__[:-4]).append(rd) for c in (self.certificate, self.future_certificate): if c: c = _make_absolute(c) # build key_info from pyxb.bundles.wssplat.ds import KeyInfo, X509Data # this assumes the file to contain a (binary) X509v3 certificate cert = open(c, "rb").read() x509 = X509Data() x509.X509Certificate = [cert] key_info = KeyInfo() key_info.X509Data = [x509] rd.KeyDescriptor.append( metadata.KeyDescriptor(key_info, use="signing")) if hasattr(rd, "NameIDFormat"): nifs = INameidFormatSupport(i) rd.NameIDFormat = nifs.supported # add role specific information -- we do not yet support all roles getattr(self, "gen_metadata_" + r)(i, rd) return ed.toxml()
def _export_own_metadata(self): """recompute our own metadata.""" from dm.saml2.pyxb import metadata ed = metadata.EntityDescriptor(entityID=self.entity_id, validUntil=utcnow() + self.metadata_validity) ld = metadata.__dict__ for r, p in self.roles.items(): if r == "ap": continue # for the moment i = self.unrestrictedTraverse(p) rd = ld[role2element[r]]() getattr(ed, rd.__class__.__name__[:-4]).append(rd) for c in (self.certificate, self.future_certificate): if c: c = _make_absolute(c) # build key_info from pyxb.bundles.wssplat.ds import KeyInfo, X509Data # this assumes the file to contain a (binary) X509v3 certificate cert = open(c, "rb").read() x509 = X509Data() x509.X509Certificate = [cert] key_info = KeyInfo() key_info.X509Data = [x509] rd.KeyDescriptor.append(metadata.KeyDescriptor(key_info, use="signing")) if hasattr(rd, "NameIDFormat"): nifs = INameidFormatSupport(i) rd.NameIDFormat = nifs.supported # add role specific information -- we do not yet support all roles getattr(self, "gen_metadata_" + r)(i, rd) return ed.toxml()
def _make_authn_assertion(self, target, req, member): from dm.saml2.pyxb.assertion import SubjectType, AuthnStatement, \ AuthnContext, AuthnContextClassRef ass = self.make_assertion() subject = self.subject_from_member(member, target, req) if not isinstance(subject, SubjectType): return subject # an error # add `SubjectConfirmation` and `AudienceRestriction` # as required by the `Web Browser SSO Profile` # We might want to put this into a mixin class as # it is helpful for any use of the HTTP-Post binding. from dm.saml2.pyxb.assertion import \ SubjectConfirmation, SubjectConfirmationData, \ Conditions, AudienceRestriction, Audience target.resolve(self._get_authority(), req) subject.SubjectConfirmation.append( SubjectConfirmation( SubjectConfirmationData( NotOnOrAfter=utcnow() + self.BROWSER_SSO_VALIDITY, Recipient=target.url, InResponseTo=req.ID, ), Method="urn:oasis:names:tc:SAML:2.0:cm:bearer")) cs = ass.Conditions = Conditions() cs.AudienceRestriction.append(AudienceRestriction(Audience( target.eid))) ass.Subject = subject # do we need `Conditions`? ass.AuthnStatement.append( AuthnStatement( AuthnContext(AuthnContextClassRef(self.authn_context_class)), AuthnInstant=ass.IssueInstant, # we cheet here )) return ass
def is_valid(self, context=None): # according to 2.5.1.1 we should implement a three-value logic # (invalid, valid, indeterminate) -- we fail to do so. now = utcnow() cv = self.NotBefore if cv is not None and now < as_utc(cv): return False cv = self.NotOnOrAfter if cv is not None and now >= as_utc(cv): return False for c in self.Condition: if not c.is_valid(context): return False for c in self.AudienceRestriction: if not c.is_valid(context): return False for c in self.OneTimeUse: if not c.is_valid(context): return False for c in self.ProxyRestriction: pass return True
def _process_assertion(self, ass, context): # ensure we know the issuer -- an exception results, if not eid = ass.Issuer.value() auth = self._get_authority() auth.metadata_by_id(eid) if not ass.verified_signature(): raise SamlError("assertion %s was not signed by the issuer" % ass.ID) if not ass.is_valid(context): raise SamlError("assertion %s is not valid" % ass.ID) if context.binding == "post": # check the `SubjectConfirmation` condition of the `Browser SSO profile` ok = False for sc in ass.Subject.SubjectConfirmation: scm = sc.Method if scm != "urn:oasis:names:tc:SAML:2.0:cm:bearer": # we do not understand the method -- hope, we find one we understand continue scd = sc.SubjectConfirmationData if scd is None: continue # not valid if scd.NotOnOrAfter is None or utcnow() >= as_utc( scd.NotOnOrAfter): continue # not valid if scd.Recipient != context.rsp.Destination: continue # not valid if scd.InResponseTo != context.rsp.InResponseTo: continue # not valid # XXX Jazkarta Note: NYU is on a private network, so we get an address # mismatch here. At the point at which we want to remove # client-specific customizations, we'll have to monkey patch this # method or find some other means of overriding this check: # if scd.Address and context.zrequest.getClientAddr() != scd.Address: # continue # not valid ok = True break if not ok: raise SamlError( "subject confirmation in assertion %s is invalid" % ass.ID) subject = ass.Subject.NameID for tag in ("Statement", "AuthnStatement", "AuthzDecisionStatement", "AttributeStatement"): for s in getattr(ass, tag): # process statement -- "AttributeError", if we do not support its type getattr(self, "_process_" + tag)(subject, s)
def _make_authn_assertion(self, target, req, member): from dm.saml2.pyxb.assertion import SubjectType, AuthnStatement, AuthnContext, AuthnContextClassRef ass = self.make_assertion() subject = self.subject_from_member(member, target, req) if not isinstance(subject, SubjectType): return subject # an error # add `SubjectConfirmation` and `AudienceRestriction` # as required by the `Web Browser SSO Profile` # We might want to put this into a mixin class as # it is helpful for any use of the HTTP-Post binding. from dm.saml2.pyxb.assertion import ( SubjectConfirmation, SubjectConfirmationData, Conditions, AudienceRestriction, Audience, ) target.resolve(self._get_authority(), req) subject.SubjectConfirmation.append( SubjectConfirmation( SubjectConfirmationData( NotOnOrAfter=utcnow() + self.BROWSER_SSO_VALIDITY, Recipient=target.url, InResponseTo=req.ID ), Method="urn:oasis:names:tc:SAML:2.0:cm:bearer", ) ) cs = ass.Conditions = Conditions() cs.AudienceRestriction.append(AudienceRestriction(Audience(target.eid))) ass.Subject = subject # do we need `Conditions`? ass.AuthnStatement.append( AuthnStatement( AuthnContext(AuthnContextClassRef(self.authn_context_class)), AuthnInstant=ass.IssueInstant, # we cheet here ) ) return ass
def _process_assertion(self, ass, context): # ensure we know the issuer -- an exception results, if not eid = ass.Issuer.value() auth = self._get_authority() auth.metadata_by_id(eid) if not ass.verified_signature(): raise SamlError("assertion %s was not signed by the issuer" % ass.ID) if not ass.is_valid(context): raise SamlError("assertion %s is not valid" % ass.ID) if context.binding == "post": # check the `SubjectConfirmation` condition of the `Browser SSO profile` ok = False for sc in ass.Subject.SubjectConfirmation: scm = sc.Method if scm != "urn:oasis:names:tc:SAML:2.0:cm:bearer": # we do not understand the method -- hope, we find one we understand continue scd = sc.SubjectConfirmationData if scd is None: continue # not valid if scd.NotOnOrAfter is None or utcnow() >= as_utc(scd.NotOnOrAfter): continue # not valid if scd.Recipient != context.rsp.Destination: continue # not valid if scd.InResponseTo != context.rsp.InResponseTo: continue # not valid if scd.Address and context.zrequest.getClientAddr() != scd.Address: continue # not valid ok = True break if not ok: raise SamlError("subject confirmation in assertion %s is invalid" % ass.ID) subject = ass.Subject.NameID for tag in ("Statement", "AuthnStatement", "AuthzDecisionStatement", "AttributeStatement"): for s in getattr(ass, tag): # process statement -- "AttributeError", if we do not support its type getattr(self, "_process_" + tag)(subject, s)
def get_authentication_session(self, request): info = self._get_cookie(request, self.session_cookie_name) if info is None: return if info["valid_until"] is not None: if utcnow() >= info["valid_until"]: return # no longer valid return info
def __init__(self, *args, **kw): if 'Version' not in kw: kw['Version'] = '2.0' # Note: an id must start with a char if 'ID' not in kw: kw['ID'] = '_' + uuid() if 'IssueInstant' not in kw: kw['IssueInstant'] = utcnow() super(_StandardSaml2Initialization, self).__init__(*args, **kw)