Compare commits
	
		
			3 Commits
		
	
	
		
			07c662b1e8
			...
			2f2cb1c337
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 2f2cb1c337 | |||
| ddab4df55f | |||
| 6e757dd0b8 | 
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,3 +7,11 @@ __pycache__/
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# Ignore prototype scripts
 | 
					# Ignore prototype scripts
 | 
				
			||||||
prototype_*.py
 | 
					prototype_*.py
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Shell secrets
 | 
				
			||||||
 | 
					*.secret
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Certificate files
 | 
				
			||||||
 | 
					*.pem
 | 
				
			||||||
 | 
					*.key
 | 
				
			||||||
 | 
					*.crt
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										28
									
								
								sk/azure.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								sk/azure.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					"""
 | 
				
			||||||
 | 
					Minimal Authentication package for Azure.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Uses client credentials - a secret or a certificate.
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import os
 | 
				
			||||||
 | 
					import requests
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def secret_credentials_auth(
 | 
				
			||||||
 | 
					        scope: str = "https://app.vssps.visualstudio.com/.default",
 | 
				
			||||||
 | 
					        tenant_id: str = os.environ.get("AZURE_TENANT_ID", ""),
 | 
				
			||||||
 | 
					        client_id: str = os.environ.get("AZURE_CLIENT_ID", ""),
 | 
				
			||||||
 | 
					        client_secret: str = os.environ.get("AZURE_CLIENT_SECRET")
 | 
				
			||||||
 | 
					        ) -> str:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Authenticate using client credentials. Pass credentials via environment variables,
 | 
				
			||||||
 | 
					    or directly as function parameters.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    token_url = f"https://login.microsoftonline.com/{tenant_id}/oauth2/v2.0/token"
 | 
				
			||||||
 | 
					    r = requests.get(token_url, data={
 | 
				
			||||||
 | 
					        "grant_type": "client_credentials",
 | 
				
			||||||
 | 
					        "client_id": client_id,
 | 
				
			||||||
 | 
					        "client_secret": client_secret,
 | 
				
			||||||
 | 
					        "scope": scope
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    r.raise_for_status()
 | 
				
			||||||
 | 
					    return r.json().get("access_token", "")
 | 
				
			||||||
							
								
								
									
										86
									
								
								sk/certificates.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								sk/certificates.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					import datetime
 | 
				
			||||||
 | 
					from cryptography.x509 import Name, NameAttribute, CertificateBuilder, BasicConstraints, random_serial_number
 | 
				
			||||||
 | 
					from cryptography.x509.oid import NameOID
 | 
				
			||||||
 | 
					from cryptography.hazmat.primitives import hashes, serialization
 | 
				
			||||||
 | 
					from cryptography.hazmat.primitives.asymmetric import rsa
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def create_self_signed_certificate(
 | 
				
			||||||
 | 
					        file_path: str,
 | 
				
			||||||
 | 
					        subject_name: str,
 | 
				
			||||||
 | 
					        organization_name: str,
 | 
				
			||||||
 | 
					        country_name: str,
 | 
				
			||||||
 | 
					        valid_days: int = 365,
 | 
				
			||||||
 | 
					        key_size: int = 2048
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Create a self-signed certificate. It saves the certificate and private key
 | 
				
			||||||
 | 
					    in PEM format to the specified file path. Three files are created:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - <file_path>.crt : The public certificate
 | 
				
			||||||
 | 
					    - <file_path>.key : The private key
 | 
				
			||||||
 | 
					    - <file_path>.pem : The certificate and private key combined in one file
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param file_path: Base file path to save the certificate and key.
 | 
				
			||||||
 | 
					    :param subject_name: Common Name (CN) for the certificate.
 | 
				
			||||||
 | 
					    :param organization_name: Organization Name (O) for the certificate.
 | 
				
			||||||
 | 
					    :param country_name: Country Name (C) for the certificate.
 | 
				
			||||||
 | 
					    :param valid_days: Number of days the certificate is valid for.
 | 
				
			||||||
 | 
					    :param key_size: Size of the RSA key.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Use the following command to replace any credentials already defined for the 
 | 
				
			||||||
 | 
					    App Registration with that certificate:
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    az ad app credential reset --id <CLIENT_ID> --cert @<file_path>.crt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Use --append to add the certificate without removing existing credentials.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    > Note: Do not upload the private key file (.key) nor the combined PEM file (.pem).
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    key = rsa.generate_private_key(public_exponent=65537, key_size=key_size)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    subject = issuer = Name([
 | 
				
			||||||
 | 
					        NameAttribute(NameOID.COUNTRY_NAME, country_name),
 | 
				
			||||||
 | 
					        NameAttribute(NameOID.ORGANIZATION_NAME, organization_name),
 | 
				
			||||||
 | 
					        NameAttribute(NameOID.COMMON_NAME, subject_name),
 | 
				
			||||||
 | 
					    ])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    cert = (
 | 
				
			||||||
 | 
					        CertificateBuilder()
 | 
				
			||||||
 | 
					        .subject_name(subject)
 | 
				
			||||||
 | 
					        .issuer_name(issuer)
 | 
				
			||||||
 | 
					        .public_key(key.public_key())
 | 
				
			||||||
 | 
					        .serial_number(random_serial_number())
 | 
				
			||||||
 | 
					        .not_valid_before(datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=1))
 | 
				
			||||||
 | 
					        .not_valid_after(datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=valid_days))
 | 
				
			||||||
 | 
					        .add_extension(BasicConstraints(ca=True, path_length=None), critical=True)
 | 
				
			||||||
 | 
					        .sign(key, hashes.SHA256())
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    crt_path = pathlib.Path(file_path).with_suffix('.crt')
 | 
				
			||||||
 | 
					    key_path = pathlib.Path(file_path).with_suffix('.key')
 | 
				
			||||||
 | 
					    pem_path = pathlib.Path(file_path).with_suffix('.pem')
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public_key = cert.public_bytes(serialization.Encoding.PEM)
 | 
				
			||||||
 | 
					    private_key = key.private_bytes(
 | 
				
			||||||
 | 
					        encoding=serialization.Encoding.PEM,
 | 
				
			||||||
 | 
					        format=serialization.PrivateFormat.TraditionalOpenSSL,
 | 
				
			||||||
 | 
					        encryption_algorithm=serialization.NoEncryption(),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    # Write the certificate
 | 
				
			||||||
 | 
					    with open(crt_path, "wb") as f:
 | 
				
			||||||
 | 
					        f.write(public_key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # Write the private key
 | 
				
			||||||
 | 
					    with open(key_path, "wb") as f:
 | 
				
			||||||
 | 
					        f.write(private_key)
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    with open(pem_path, "wb") as f:
 | 
				
			||||||
 | 
					        f.write(public_key)
 | 
				
			||||||
 | 
					        f.write(private_key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print(f"Certificate created and saved.")
 | 
				
			||||||
 | 
					    print(f" - Certificate: {crt_path}")
 | 
				
			||||||
 | 
					    print(f" - Private Key: {key_path}")
 | 
				
			||||||
 | 
					    print(f" - PEM File: {pem_path}")
 | 
				
			||||||
		Reference in New Issue
	
	Block a user