Download saml/sp/authn_request.py from Snapkitty/vault-live: direct link, hf CLI and curl.
- Browser
- Download file 3.62 kB
-
https://huggingface.co/Snapkitty/vault-live/resolve/main/saml/sp/authn_request.py
- Command line
-
hf download hf://Snapkitty/vault-live/saml/sp/authn_request.py
-
curl -L -o authn_request.py https://huggingface.co/Snapkitty/vault-live/resolve/main/saml/sp/authn_request.py
3.62 kB
| """ | |
| saml/sp/authn_request.py | |
| SAML AuthnRequest builder: XML construction, C14N, RSA-SHA256 signing, | |
| Base64 + deflate encoding for HTTP-Redirect binding. | |
| """ | |
| import base64 | |
| import uuid | |
| import zlib | |
| from dataclasses import dataclass | |
| from datetime import datetime, timezone | |
| from xml.etree import ElementTree as ET | |
| from crypto.xml_dsig import sign_element, exclusive_c14n | |
| SAML_NS = "urn:oasis:names:tc:SAML:2.0:assertion" | |
| SAMLP_NS = "urn:oasis:names:tc:SAML:2.0:protocol" | |
| _SA = "{%s}" % SAML_NS | |
| _SAP = "{%s}" % SAMLP_NS | |
| REDIRECT_BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" | |
| POST_BINDING = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" | |
| class AuthnRequestConfig: | |
| sp_entity_id: str | |
| idp_sso_url: str | |
| acs_url: str | |
| private_key: object # RSAPrivateKey | |
| certificate: object # x509.Certificate | |
| class SignedRequest: | |
| xml_element: ET.Element | |
| redirect_url: str | |
| request_id: str | |
| post_value: str | |
| class AuthnRequestBuilder: | |
| def __init__(self, config: AuthnRequestConfig): | |
| self.config = config | |
| def build(self) -> ET.Element: | |
| request_id = '_' + uuid.uuid4().hex | |
| now = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ') | |
| root = ET.Element(_SAP + 'AuthnRequest') | |
| root.set('ID', request_id) | |
| root.set('Version', '2.0') | |
| root.set('IssueInstant', now) | |
| root.set('Destination', self.config.idp_sso_url) | |
| root.set('ProtocolBinding', POST_BINDING) | |
| root.set('AssertionConsumerServiceURL', self.config.acs_url) | |
| issuer = ET.SubElement(root, _SA + 'Issuer') | |
| issuer.text = self.config.sp_entity_id | |
| nameid_policy = ET.SubElement(root, _SAP + 'NameIDPolicy') | |
| nameid_policy.set('AllowCreate', 'true') | |
| nameid_policy.set('Format', 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient') | |
| return root | |
| def sign(self, element: ET.Element) -> ET.Element: | |
| request_id = element.get('ID', '_unknown') | |
| sign_element(element, self.config.private_key, self.config.certificate, request_id) | |
| return element | |
| def encode_redirect(self, element: ET.Element) -> str: | |
| """Deflate + Base64 + URL-encode for HTTP-Redirect binding SAMLRequest param.""" | |
| xml_bytes = ET.tostring(element, encoding='unicode').encode('utf-8') | |
| compressed = zlib.compress(xml_bytes)[2:-4] # raw deflate (strip zlib header/trailer) | |
| b64 = base64.b64encode(compressed).decode('ascii') | |
| from urllib.parse import quote | |
| return quote(b64) | |
| def encode_post(self, element: ET.Element) -> str: | |
| """Base64-encode for HTTP-POST binding SAMLRequest field.""" | |
| xml_bytes = ET.tostring(element, encoding='unicode').encode('utf-8') | |
| return base64.b64encode(xml_bytes).decode('ascii') | |
| def build_and_sign(self) -> SignedRequest: | |
| element = self.build() | |
| request_id = element.get('ID', '_unknown') | |
| self.sign(element) | |
| redirect_encoded = self.encode_redirect(element) | |
| post_encoded = self.encode_post(element) | |
| redirect_url = ( | |
| f"{self.config.idp_sso_url}" | |
| f"?SAMLRequest={redirect_encoded}" | |
| ) | |
| return SignedRequest( | |
| xml_element=element, | |
| redirect_url=redirect_url, | |
| request_id=request_id, | |
| post_value=post_encoded, | |
| ) | |