class Keycloakconnector: keycloak_openid = None def __init__(self, serverurl, realm, clientid, secret): self.keycloak_openid = KeycloakOpenID(server_url=serverurl, client_id=clientid, realm_name=realm, client_secret_key=secret, verify=True) config_well_know = self.keycloak_openid.well_know() # print(config_well_know) def getToken(self, appID, api): token = self.keycloak_openid.token(username="", password="", grant_type=["client_credentials"]) token = token['access_token'] print("Access token = ") print(token) return token # Get JWT public key. def getJWTPublickey(self): cert = self.keycloak_openid.certs() if cert is None: return "" x5c = cert.get('keys')[0]['x5c'][0] x5c = '-----BEGIN CERTIFICATE-----\n' + x5c + '\n-----END CERTIFICATE-----' x509 = OpenSSL.crypto.load_certificate(FILETYPE_PEM, x5c) pubkey = x509.get_pubkey() pubkey = OpenSSL.crypto.dump_publickey(FILETYPE_PEM, pubkey).decode("utf-8") return pubkey
def manager_login(url, username, password): realm = 'master' if username == 'smartcity': realm = 'smartcity' keycloak_openid = KeycloakOpenID( server_url=f'https://{url}/auth/', client_id="admin-cli", realm_name=realm, client_secret_key="secret", ) if password: response = keycloak_openid.token(username, password) else: password = config.get_password(url, username) response = keycloak_openid.token(username, password) config.store_token(url, username, password, response['refresh_token'])
def get_common_services_access_token(config): # Configure Keycloak client keycloak_openid = KeycloakOpenID(server_url=config.COMM_SERV_AUTH_URL, client_id=config.COMM_SERV_CLIENT_ID, realm_name=config.COMM_SERV_REALM, client_secret_key=config.COMM_SERV_CLIENT_SECRET) # Get Token token = keycloak_openid.token('', '', 'client_credentials') return token['access_token']
def interactive_login(realm_label): """Logs in interactively.""" env, config = utils.get_selected_deployment_config() if config is not None and URL_KEY in config: nxs.config.set_environment(config[URL_KEY]) else: utils.error( "You must first select a profile using the 'profiles' command") issuer = '' if realm_label: try: response = nxs.realms.fetch(realm_label) issuer = response['_issuer'] except nxs.HTTPError as e: print(e.response.json()) utils.error(str(e)) else: realm = utils.get_default_realm() if realm is None: realm = _select_realm() issuer = realm['_issuer'] label = realm['_label'] print("Saving realm '%s' in your profile" % realm['_label']) utils.set_default_realm(label, issuer) else: issuer = realm['_issuer'] print( "Using default realm '%s' set in your profile. Use --realm to override." % realm['_label']) default_client_id = utils.get_default_client_id() client_id = input("Please enter the client ID [%s]: " % default_client_id) if not client_id: client_id = default_client_id utils.set_default_client_id(client_id) detected_user = getpass.getuser() username = input("Username [%s]: " % detected_user) if not username: username = detected_user password = getpass.getpass() try: url, name = issuer.split("realms/") keycloak_openid = KeycloakOpenID(server_url=url, client_id=client_id, realm_name=name, verify=True) response = keycloak_openid.token(username, password) token = response['access_token'] _set_token(token) utils.success( "Authentication successful. Use 'view-token' to show your credentials." ) except KeycloakError as e: utils.error("Authentication failed: %s" % e)
def get_kc_token(): keycloak_openid = KeycloakOpenID( server_url = config.gpap_token_auth['server_url'], client_id = config.gpap_token_auth['client_id'], realm_name = config.gpap_token_auth['realm_name'], client_secret_key = config.gpap_token_auth['client_secret_key'], verify = False ) token = keycloak_openid.token(config.gpap_token_required[1], config.gpap_token_required[2]) return token
def get_keycloak_token(configs): """Retrieves Keycloak bearer token""" keycloak_openid = KeycloakOpenID( server_url=configs['url'], client_id=configs['client_id'], realm_name=configs['realm_name'], client_secret_key=None, verify=configs['secure']) return keycloak_openid.token(configs['username'], configs['password'])
def get_token(auth_url: str, auth_client_id: str, auth_realm: str): username = args.username password = args.password from keycloak import KeycloakOpenID keycloak_openid = KeycloakOpenID(server_url=auth_url, client_id=auth_client_id, realm_name=auth_realm) # Get Token token = keycloak_openid.token(username, password) return token, keycloak_openid
def authenticate(cls, username=None, password=None): """ Return 2tuple, representing authorized user or (None,None) on fail First tuple element is String ID representing a distingushed name for external users, e.g.: "[email protected],ou=People,o=warehouse-external" Second tuple element is Dict, representing a decoded ODIC access token >>> b = KeycloakBackend() >>> b.authenticate() (None, None) >>> # Check username >>> b.authenticate("*****@*****.**", "anything") (None, None) >>> # Check bogus username >>> b.authenticate("Test", "anything") (None, None) """ user_identifier = None decoded_token = None if username is not None: u = None #auth may have failed: prepare to return None # setup OIDC client conf = config_loader.get_api_config() auth_url = conf['external.keycloak.url'] dw_client_id = conf['external.keycloak.warehouse_client_id'] auth_client = KeycloakOpenID(server_url=auth_url ,client_id=dw_client_id, realm_name="master" ,client_secret_key="value-doesnt-matter" ,verify=False) try: # login encoded_token = auth_client.token(username, password) # get server public key iss_url = auth_url+'realms/master' json_iss = requests.get(iss_url, verify=False).json() pub_key = add_pem_headfoot(json_iss['public_key']) # decode server response decode_opts = {"verify_signature":True, "verify_aud": True, "exp": True} decoded_token = auth_client.decode_token( encoded_token['access_token'] ,key=pub_key, options=decode_opts) user_dn = oidc_token_to_ldap_style_name(decoded_token) #login is valid user_identifier = user_dn #return except KeycloakAuthenticationError: pass #login is invalid return user_identifier, decoded_token
def get_token(envs): username = args.username password = args.password from keycloak import KeycloakOpenID print(f"Auth Url: {envs['auth']['url']}") print(f"Auth ClientId: {envs['auth']['clientId']}") print(f"Auth Realm: {envs['auth']['realm']}") keycloak_openid = KeycloakOpenID(server_url=envs['auth']['url'], client_id=envs['auth']['clientId'], realm_name=envs['auth']['realm']) # Get Token token = keycloak_openid.token(username, password) return token, keycloak_openid
def keycloak_handler(keycloak_config): """ Keycloak token is required for many API calls """ # Configure client keycloak_openid = KeycloakOpenID( server_url=keycloak_config['url'], client_id=get_envar(keycloak_config['client_id']), realm_name=keycloak_config['realm'], client_secret_key=get_envar(keycloak_config['client_secret'])) # Get Token token = keycloak_openid.token(get_envar(keycloak_config['user']), get_envar(keycloak_config['password'])) token = token['access_token'] return {'Authorization': f'Bearer {token}'}
def login(): # Configure client keycloak_openid = KeycloakOpenID(server_url="http://10.255.230.159:8181/auth/", client_id="calls-gateway", realm_name="ambulance-example", client_secret_key="supersecret") # Get WellKnow # config_well_know = keycloak_openid.well_know() # Get Token token = keycloak_openid.token("user1", "user1") # token = keycloak_openid.token("user", "password", totp="012345") # Get Userinfo # userinfo = keycloak_openid.userinfo(token['access_token']) return token
def is_valid_token(self): credentials = self.__parse_token(); username = credentials[1] pwd = credentials[2] keycloak = KeyCloak(server_url="https://auth.s2s-omxware.us-south.containers.appdomain.cloud/auth/", client_id="omx-zeppelin", realm_name="omxware", client_secret_key="c05b7553-cf21-4f0c-ab81-a38aca3ba172", verify=False) if self.__env != 'public': keycloak = KeyCloak(server_url="https://omx-auth.sl.cloud9.ibm.com/auth/", client_id="omx-zeppelin", realm_name="omxware", client_secret_key="1320e78d-025d-48eb-ad3e-451281786932", verify=False) try: # Get Token token = keycloak.token(username, pwd) self.__userinfo = keycloak.userinfo(token['access_token']) self.__keycloak_token = token['access_token'] return True except KeycloakAuthenticationError as auth_error: # Exception object looks like this # keycloak.exceptions.KeycloakAuthenticationError: # 401: b'{"error":"invalid_grant","error_description":"Invalid user credentials"}' error_msg = '' if auth_error['error_description'] != None: error_msg = auth_error['error_description'] if error_msg.strip() != None: print(error_msg) else: print(auth_error['error_description']) return False
from io import StringIO print(" ") print("=================") print("=== SSO LOGIN ===") print("=================") print(" ") keycloak_openid = KeycloakOpenID( server_url="https://service.wienerstadtwerke.at/auth/", client_id="client-smp-public", realm_name="wienernetze", client_secret_key=uuid.uuid1()) try: token = keycloak_openid.token(USERNAME, PASSWORD) except: try: print('[' + str(sys.exc_info()[1]).split(":")[0] + '] ' + requests.status_codes._codes[int( str(sys.exc_info()[1]).split(":")[0])][0]) except: print("Network ERROR") print(" ") sys.exit() userinfo = keycloak_openid.userinfo(token['access_token']) print('[200] ' + str(userinfo)) print(" ") print("===============")
class NDMWS: ''' Main class to talk to NDM Web Services. NDM public web services are at endpoints: - /api/mprc: MAVProxy Remote Console services (add, remove, list and update data) - /api/rmp: Reactive Mission Planner services (send or retrieve subplan, get GPS pos) In order to use these service, an authentication bearer is required that should be sent along with the request to any service. The NDMWS library helps with this authentication and provides several shortcuts to common sequences of service requests. A tipical use of the library could be: 1. Connect to Keycloak (retrieve the authentication bearer) 2. Create a custom subplan 3. Send the subplan to the RMP Other tipical (shorter) use could be: 1. Connect to Keycloak (retrieve the authentication bearer) 2. Trigger a predefined alarm (with its mapped subplan) In this last case, the alarm is only a label identifying a predefined mapping that we want to call use. This requires an association between the alarm and a subplan that should be defined in the configuration file in advance. The alarm is then sent to the vMPA and the mapped subplan is sent to the vDFC for execution. ''' def __init__(self, dfcaddr=None, droneid=None, mpaaddr=None, inifile='ndmws.ini', debug=False): ''' Creates an instance of the helper to call NDM Web Services. This is the first object to instantiate and the one to start WS interaction. You can specify `dfcaddr`, `mpaaddr` and a `droneid` or you can leave it blank. In this case, you an alternativelly inform the `inifile` name or leave the defaults. Debug is `False` by default, so you'll have to set `debug=True` to see config variables, i.e. ''' global DEBUG DEBUG = debug config.read(inifile) if dfcaddr == None: _('Using config: %s' % inifile) self.dfcaddr = config.get('dfc', 'hostport') else: self.dfcaddr = dfcaddr if mpaaddr == None: self.mpaaddr = config.get('mpa', 'hostport') _('Using mpaaddr: %s' % self.mpaaddr) else: self.mpaaddr = mpaaddr if droneid == None: self.droneid = config.get('dfc', 'droneid') _('Using droneid: %s' % self.droneid) else: self.droneid = droneid self.logger = logging.getLogger(__name__) # create console handler with a higher log level ch = logging.StreamHandler() ch.setLevel(logging.INFO) # create formatter and add it to the handlers formatter = logging.Formatter( "%(asctime)s.%(msecs)03d[%(levelname)-8s]:%(created).3f %(message)s", datefmt="%Y-%m-%d %H:%M:%S") ch.setFormatter(formatter) # add the handlers to logger if (self.logger.hasHandlers()): self.logger.handlers.clear() self.logger.addHandler(ch) self.logger.propagate = False self.authenticate() self.last_t_subplan = 0 # self.relay_bcp_client = RelayClient( # password=PASSWORD, # keyfile=KEYFILE, # identity=IDENTITY, # can be set afterwards by calling `client.set_identity(identity)` # tx_relay_address=TX_RELAY_ADDRESS, # meta_identity_manager_address=META_IDENTITY_MANAGER_ADDRESS, # entity_data_manager_address=ENTITY_DATA_MANAGER_ADDRESS, # relay_server_address=RELAY_ENDPOINT,) # self.vmme_producer = None # self.initialized_vmme = False def authenticate(self, hostport=None, username=None, password=None): ''' Authentication with Keycloak is required before any API interaction. Realm is hardcoded to 'ndmws' and client 'ndmws-client', so no other configuration is required. Note that the communication is always encrypted so no plain text is on the wire, but you should protect your configuration file from unathorized access (i.e. don't ever commit your ndmws.ini file in your repo). ''' if hostport == None or username == None or password == None: hostport = config.get('auth', 'hostport') _('Using auth hostport: %s' % hostport) username = config.get('auth', 'username') _('Using auth username: %s' % username) password = config.get('auth', 'password') _('Using auth password: %s' % password) _('Using auth url: %s' % (AUTHURL % hostport)) self.kc = KeycloakOpenID(server_url=AUTHURL % hostport, client_id='ndmws-client', realm_name='ndmws') self.token = self.kc.token(username, password) self.logger.info(self.token) def userinfo(self): ''' Get keycloak user info from the identity server. This is only required if you want to be sure that the user you are authenticating with is the one allowed to interact with the web services, also no estrictly neccessary. ''' return self.kc.userinfo(self.token['access_token']) def getpos(self, ts, th): ''' Get the GPS position of the flying drone when time = `ts`. While the drone (in config) is flying, every instant can be resolved to a known GPS position. This method retrieves the GPS position from the done `traces` before the drone lands. The opposite is not true, since the drone might (and usually does) pass twice or more to the same GPS position. ''' auth = self.token['access_token'] trace_url = TRACEURL % (self.dfcaddr, self.droneid, ts, th) r = get(trace_url, headers={'Authorization': 'Bearer %s' % auth}) self.logger.info("RESPONSE GETPOS: {}".format(r.text)) if r.status_code == 200: j = loads(r.text) return {'time': j['time'], 'pos': j['pos']} else: return {'code': r.status_code, 'msg': r.json()['msg']} def sendplan(self, data): ''' Send a subplan to the vDFC for a subplan change. With this method you can arbitrarily send a hand-made subplan (JSON) or a plan constructed using the `Subplan` fluent language builder. The plan will be executed only if the drone is not currently executing a subplan. If the drone is yet executing a subplan, an 409 error will be issued, but you can catch and ignore it as you wish, since no exception is raised at this moment. ''' auth = self.token['access_token'] r = put(SUBPLANURL % (self.dfcaddr, self.droneid), json=loads(data) if type(data) == str else data, headers={'Authorization': 'Bearer %s' % auth}) self.logger.info('SUBPLAN RESPONSE: {}'.format(r.text)) if r.status_code == 200: return r.text else: return {'code': r.status_code, 'msg': r.json()['msg']} def sendalarm(self, src, obj, ts): ''' Send an alarm to the vMPA for notification. Use this method to send a notification to the vMPA party. This notification could be sent in parallel to a subplan change (i.e. senplan) in the vDFC, so that both parties are notified of the alarm at the same time or sent by itself when an uploaded video is being processed. The 'src' param should be self.droneid if the video source comes from a drone live video or the label id (user supplied when a video is uploaded. 'object' identification and actual time 'ts' should also be passed as a way to enrich the alert message. ''' auth = self.token['access_token'] r = put(ALARMURL % (self.mpaaddr, src, obj, ts), headers={'Authorization': 'Bearer %s' % auth}) self.logger.info("Response ALARM:{}".format(r.text)) if r.status_code == 200: return r.text else: return {'code': r.status_code, 'msg': r.json()['msg']} def triggeralarm(self, alarm, t, subplan_time_space=60, send_subplan=False, threshold=GPS_POS_THRESHOLD): ''' Trigger an alarm and send a predefined subplan to the RMP (Reactive Mission Planner). Predefined subplans could be configured in the configuration file easily following this structure: [alarmmap] ALARM = SP_SUBPLAN_NAME By adding a mapping between an alarm name and a suplan name, the library knows which subplan should be loaded when an alarm is triggered. [ALARM] reason = reason_text object = object_text A new section should be added for every new alarm, specifying two keys, `reason` and `object` that will be substituted into every subplan mapped to this alarm. [subplans] SP_SUBPLAN_NAME = { subplan: [ ... ] } A new key should be added to the `subplans` section to write the JSON corresponding to the subplan. Of course, the subplan object can (and should) include substitution variables that need to be escaped (use a double `%`) to avoid early template subtitution. Allowed template variables are as follows: - clientid - reason - obj - lat - lon - alt ''' try: resp = self.getpos(t, threshold) if 'code' in resp: # self.logger.info('Exception of drone not flying') raise Exception('%d: %s' % (resp['code'], resp['msg'])) else: _('Event: %s' % alarm) sp = config.get('alarmmap', alarm) _('Subplan name: %s' % sp) if sp == None: raise Exception('ERROR: alarm `%s` has no mapped subplan' % alarm) subplan = config.get('subplans', sp) _('Subplan: %s' % subplan) reason = config.get(alarm, 'reason') obj = config.get(alarm, 'object') _('Sending alert of object %s at time %d' % (obj, t)) self.sendalarm(self.droneid, obj, t) if t > (self.last_t_subplan + subplan_time_space * 1000) and send_subplan: clientid = config.get('client', 'clientid') params = resp['pos'] params.update({ 'clientid': clientid, 'reason': reason, 'obj': obj }) _('Subplan params: %s' % params.__str__()) self.logger.info(('Subplan params: %s' % params.__str__())) subplan = subplan % params _('Sending subplan: %s' % subplan) # vBCP client # params["ts"] = t # self.send_bcp_data(params) # vMME client # self.send_mme_data(params) # self.last_t_subplan = t self.logger.info("sending subplan") return self.sendplan(subplan) else: # if (int((t-self.last_t_subplan)*0.001)) % 10 == 0: # self.logger.info('No subplans allowed. Last one sent {}(s) ago'.format(int((t-self.last_t_subplan)*0.001))) return {'code': 200, 'msg': 'No subplans allowed'} except Exception: pass def send_bcp_data(self, data): payload = dumps(data) # Relay the payload as a signed message to the Relay Server. The message will be forwarded to the vMCM. self.logger.info( 'Relaying signed payload to the server: {}'.format(payload)) # status = self.relay_bcp_client.relay_signed_message(payload) # self.logger.info('Got status code : {}'.format(status)) tx_hash = self.relay_bcp_client.register_mpa_data(payload) self.logger.info('Got transaction hash : {}'.format(tx_hash)) self.logger.info("data has been sent to vBCP") # def init_mme(self, data): # device_data = self.generate_mme_data(data) # send_init_msg_influx(device_data) def generate_mme_data(self, data): topic = generate_mqtt_topics() device_data = { "device_id": "DEVICE_ID_VMPA", "device_name": "DEVICE_NAME_VMPA", "device_ip": self.droneid, "lat": data['lat'], "lon": data['lon'], "prev_mme": "none", "sender": "device", "device_topic": topic, # data["topic"] if "topic" in data.keys() else data["reason"], "entity_type": "drone" } return device_data def send_mme_data(self, data): db_path = "vmme_client/device.db" device_data = self.generate_mme_data(data) self.write_mme_data(db_path, device_data) self.logger.info("Data has been sent to vMME") def write_mme_data(self, db_path, device_data): self.logger.info("On write_mme_data") self.logger.info(self.initialized_vmme) if self.initialized_vmme == False: self.logger.info("TO INITIALIZE VMME") initialize_vmme(db_path, device_data) # send_init_msg_influx(device_data) # FIRST_BROKER = select(db_path, device_data["device_id"]) # # if DEMO: # # try: # # base = BaseClient(password=password, # # keyfile="vmme_client/keyfile.json") # # base.create_keyfile() # # vmme_wallet_addr = base._fetch_signer_address() # # identity_hash_response = create_identity(vmme_wallet_addr) # # identity_hash = identity_hash_response.json() # # valid_identity(identity_hash["tx_hash"].strip(), vmme_wallet_addr, device_data["entity_type"]) # # self.logger.info("The device registration to the vAAA was successful: \n") # # except Exception as e: # # self.logger.info(e) # insert_ip(db_path, FIRST_BROKER) # insert_device_data(db_path, device_data) self.vmme_producer = Producer() self.initialized_vmme = True self.logger.info("Initialized vMME") if self.vmme_producer: self.vmme_producer.operate(device_data["device_id"]) self.logger.info("Out of write mme data")
def auto_test(data_frame, sso, api, website, user, password, prefix, logger, db_var, dashboards, add_cookies, custom_headers, headers, var_type='default', keycloak=False): """ Key function to start and poll metrics on selected instance via API :param (str) var_type: variables use from "logs" or "default" :param (pandas.core.frame.DataFrame) data_frame: existing metrics from prod :param (str) sso: url to sso :param (str) api: url to api :param (str) website: DNS :param user: username :param password: password :param (str) prefix: prefix like 'prod' or 'qa' :param logger: logger :param db_var: db for getting variables :param (str) dashboards: collection with dashboard :param (bool) add_cookies: if True then add '_currentUser' and 'roles' cookies :param (bool) custom_headers: if True then add custom headers :param (dict) headers: dictionary with custom headers :param (bool) keycloak: if keycloak authorization is active :return: """ if keycloak: keycloak_openid = KeycloakOpenID( server_url=config.server_url, client_id=config.client_id, realm_name=config.realm_name, client_secret_key=config.client_secret_key) token = keycloak_openid.token(user.split('@')[0], password) headers = { 'Authorization': 'Bearer {}'.format(str(token['access_token'])) } session = requests.Session() session.headers.update(headers) else: session = run_sso_session(sso, user, password, logger, add_cookies, custom_headers, headers) result = pd.DataFrame() for i in range(0, data_frame.shape[0]): dashboard_id = data_frame.loc[i, 'dashboard_id'] metric_id = data_frame.loc[i, 'metric_id'] aggregation = collect_parameters(i, dashboard_id, metric_id, logger, user, db_var, dashboards, var_type) # формирование тела запроса метрики body = { "__content": { "type": "metricData", "parameters": { "dashboardId": "{}".format(dashboard_id), "metricId": "{}".format(metric_id), "variables": aggregation['variables'] } } } # отправка запроса try: start_job = session.post(api + 'startJob', json=body, timeout=(12, 20)) except requests.exceptions.Timeout: start_job = 'start_job_timeout' logger.info('{} at metric {}'.format(start_job, metric_id)) result = write_results(result, i, dashboard_id, metric_id, str(start_job), 0, 400, website, prefix) sleep(120) break logger.info("metric {} run with code {} by {}".format( metric_id, int(start_job.status_code), user)) if int(start_job.status_code) in [403, 500]: logger.info("metric {} failed with code {}".format( metric_id, int(start_job.status_code))) result = write_results(result, i, dashboard_id, metric_id, str(start_job.text), 0, int(start_job.status_code), website, prefix) elif int(start_job.status_code) in [404, 502]: logger.info("metric {} failed with code {}".format( metric_id, int(start_job.status_code))) result = write_results(result, i, dashboard_id, metric_id, str(start_job.text), 0, int(start_job.status_code), website, prefix) sleep(120) else: try: job = start_job.json()['id'] except json.decoder.JSONDecodeError: try: logger.info("metric {} failed with code {}".format( metric_id, int(start_job.status_code))) except ValueError: logger.info("metric {} failed with code {}".format( metric_id, start_job.text)) break # Получение ответа на запрос sleeper = time() poll_job = 'poll_job_timeout' while time() - sleeper <= 121: try: poll_job = session.get(api + 'pollJob/' + job, timeout=(12, 20)) logger.info("metric {} polled with code {}".format( metric_id, int(poll_job.status_code))) except requests.exceptions.ReadTimeout or requests.exceptions.ConnectionError: logger.info('{} at metric {}'.format(poll_job, metric_id)) sleep(120) break if int(poll_job.status_code) != 204: logger.info( "metric {} polled with code {} in {} sec".format( metric_id, int(poll_job.status_code), int(time() - sleeper))) break sleep(1) if poll_job == 'poll_job_timeout': text = poll_job status_code = 204 else: text = read_poll_job(poll_job) status_code = int(poll_job.status_code) result = write_results(result, i, dashboard_id, metric_id, text, int(time() - sleeper), status_code, website, prefix) return result
from keycloak import KeycloakOpenID # Configure client keycloak_openid = KeycloakOpenID(server_url="http://localhost:7501/auth/", client_id="oidc-native-app-client", realm_name="realm0") # Get WellKnow config_well_know = keycloak_openid.well_know() # Get Token token = keycloak_openid.token("user0", "password0") print (token['access_token']) # Get Userinfo userinfo = keycloak_openid.userinfo(token['access_token']) print(userinfo) # Refresh token token = keycloak_openid.refresh_token(token['refresh_token']) # Logout keycloak_openid.logout(token['refresh_token'])
LHCR1_CLIENT_ID = "testviewer" LHCR1_CLIENT_SECRET = "85ac8b3e-df3c-4e4f-be65-ca5e5b68d3dc" LHCR2_SERVER_URL = "http://192.168.0.36:8082/auth/" LHCR2_REALM_NAME = "lhcr2" LHCR2_CLIENT_ID = "lhcr1_testviewer" LHCR2_CLIENT_SECRET = "91568b49-bb6d-4197-90f7-132e9db5fe16" #Get a token from lhcr1 lhcr1oidc = KeycloakOpenID(server_url=LHCR1_SERVER_URL, client_id=LHCR1_CLIENT_ID, realm_name=LHCR1_REALM_NAME, client_secret_key=LHCR1_CLIENT_SECRET, verify=True) lhcr1token = lhcr1oidc.token("user1", "user1") print(lhcr1token) #Exchange it for a token from lhcr2 lhcr2oidc = KeycloakOpenID(server_url=LHCR2_SERVER_URL, client_id=LHCR2_CLIENT_ID, realm_name=LHCR2_REALM_NAME, client_secret_key=LHCR2_CLIENT_SECRET, verify=True) lhcr2token = exchange_token(lhcr2oidc, lhcr1token['access_token'], "lhcr1oidc") print(lhcr2token) """ curl -X POST \ -d "client_id=starting-client" \
#!/usr/bin/env python3 from keycloak import KeycloakOpenID keycloak_openid = KeycloakOpenID( server_url='http://localhost:8080/auth/', client_id='romi-dashboard', realm_name='master' ) token = keycloak_openid.token('admin', 'admin') user_id = keycloak_openid.userinfo(token['access_token'])['sub'] user_name = keycloak_openid.userinfo(token['access_token'])['preferred_username'] print(f'user ID: {user_id}') print(f'user name: {user_name}') #import requests # todo: stuff the token into the request headers #headers = {'foo': 'bar'} #r = requests.get('http://localhost:5000', headers=headers) #print(r.text)
] ) # jwt_admin_id = '8ea63bb3-32b7-4619-acbd-cc6ab267d6a4' jwt_admin_id = '18bd82e0-cfd6-434f-807c-443e6b81f18c' # jwt_user_id = '1295b697-e8d6-4125-b6f4-98992ea3f0c6' jwt_user_id = 'c44e7443-d62b-41c0-85c9-7a56a1b8c60e' headers = { 'Content-type': 'application/json' } if IS_GUARD_ENABLED: keycloak_openid = KeycloakOpenID(server_url=KEYCLOAK_URL, client_id=CLIENT_ID, realm_name=REALM_NAME) response = keycloak_openid.token(ADMIN_USERNAME, ADMIN_PASSWORD) auth_token = response['access_token'] headers['Authorization'] = f'Bearer {auth_token}' def create_user(user): response = requests.post(url=f'{NEW_USER_URL}', json=user, headers=headers) logging.info(f'New user was added. id: {response.json()["id"]}') return response.json()['id'] def create_sub_location(loc_id, sub_location): response = requests.post(url=f'{NEW_SUB_LOCATION_URL}/{loc_id}', json=sub_location, headers=headers) logging.info(f'New sub locations was added. id: {response.json()["id"]}') return response.json()['id']
user = '******' password = '******' realm_name = 'demo' client_id = 'demo_client' client_secret = 'df0eb620-53af-4c62-ae41-71941cbfa1a4' # interact with the software in various ways keycloak_openid = KeycloakOpenID(server_url=server_url, realm_name=realm_name, client_id=client_id, client_secret_key=client_secret) print("OPENID:") print(keycloak_openid) config_well_know = keycloak_openid.well_know() print("WELL KNOW:") print(config_well_know) token = keycloak_openid.token(user, password) print("TOKEN:") print(token) user_info = keycloak_openid.userinfo(token['access_token']) print("USER INFO:") print(user_info) certs = keycloak_openid.certs() print("CERTS:") print(certs)
class AuthProvider: def __init__(self, app=None): if app is not None: self.init_app(app) def init_app(self, app): self.provider = app.config['THROAT_CONFIG'].auth.provider if self.provider == 'KEYCLOAK': cfg = app.config['THROAT_CONFIG'].auth.keycloak self.keycloak_admin = KeycloakAdmin(server_url=cfg.server, realm_name=cfg.user_realm, user_realm_name=cfg.admin_realm, client_id=cfg.admin_client, client_secret_key=cfg.admin_secret, verify=True, auto_refresh_token=['get', 'put', 'post', 'delete']) self.keycloak_openid = KeycloakOpenID(server_url=cfg.server, client_id=cfg.auth_client, client_secret_key=cfg.auth_secret, realm_name=cfg.user_realm, verify=True) def get_user_by_email(self, email): try: return User.get(fn.Lower(User.email) == email.lower()) except User.DoesNotExist: try: um = UserMetadata.get((UserMetadata.key == 'pending_email') & (fn.Lower(UserMetadata.value) == email.lower())) return User.get(User.uid == um.uid) except UserMetadata.DoesNotExist: pass if self.provider == 'KEYCLOAK': users = self.keycloak_admin.get_users({"email": email}) for userdict in users: try: return User.get(User.name == userdict['username']) except User.DoesNotExist: return None return None def create_user(self, name, password, email, status=UserStatus.OK, verified_email=False): auth_source = getattr(UserAuthSource, self.provider) if self.provider == 'LOCAL': uid = str(uuid.uuid4()) crypto = UserCrypto.BCRYPT password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) else: uid = self.keycloak_admin.create_user({'email': email, 'username': name, 'enabled': True, 'emailVerified': verified_email, 'credentials': [{'value': password, 'type': 'password'}]}) password = '' crypto = UserCrypto.REMOTE user = User.create(uid=uid, name=name, crypto=crypto, password=password, status=status, email=email, joindate=datetime.utcnow()) self.set_user_auth_source(user, auth_source) self._set_email_verified(user, verified_email) return user def get_user_auth_source(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) return UserAuthSource(int(umd.value)) except UserMetadata.DoesNotExist: return UserAuthSource.LOCAL def set_user_auth_source(self, user, value): value = str(int(value)) try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="auth_source", value=value) def get_user_remote_uid(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "remote_uid")) return umd.value except UserMetadata.DoesNotExist: return user.uid def set_user_remote_uid(self, user, remote_uid): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "remote_uid")) umd.value = remote_uid umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="remote_uid", value=remote_uid) pass def change_password(self, user, old_password, new_password): if self.validate_password(user, old_password): auth_source = self.get_user_auth_source(user) if self.provider == 'LOCAL' and auth_source == UserAuthSource.LOCAL: user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) user.save() elif self.provider == 'KEYCLOAK' and auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=self.get_user_remote_uid(user), payload={'credentials': [{'value': new_password, 'type': 'password'}]}) else: raise AuthError # Invalidate other existing login sessions. User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() theuser = misc.load_user(user.uid) login_user(theuser, remember=session.get("remember_me", False)) def reset_password(self, user, new_password): auth_source = self.get_user_auth_source(user) if self.provider == 'LOCAL': if auth_source == UserAuthSource.LOCAL: user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) else: raise AuthError elif self.provider == 'KEYCLOAK': if auth_source == UserAuthSource.LOCAL: kuid = self.keycloak_admin.create_user({'email': user.email, 'username': user.name, 'enabled': True, 'emailVerified': self.is_email_verified(user), 'credentials': [{'value': new_password, 'type': 'password'}]}) self.set_user_remote_uid(user, kuid) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = '' elif auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=self.get_user_remote_uid(user), payload={'credentials': [{'value': new_password, 'type': 'password'}]}) else: raise AuthError user.save() User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() def get_pending_email(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) return umd.value except UserMetadata.DoesNotExist: return None def clear_pending_email(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.delete_instance() except UserMetadata.DoesNotExist: return None def set_pending_email(self, user, email): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.value = email umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="pending_email", value=email) def confirm_pending_email(self, user, email): umd = None try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) except UserMetadata.DoesNotExist: pass auth_source = self.get_user_auth_source(user) if self.provider == 'LOCAL': if auth_source != UserAuthSource.LOCAL: raise AuthError elif self.provider == 'KEYCLOAK': if auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=self.get_user_remote_uid(user), payload={'email': email, 'emailVerified': True}) else: raise AuthError if umd is not None: umd.delete_instance() user.email = email user.save() self._set_email_verified(user) def is_email_verified(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) return umd.value == '1' except UserMetadata.DoesNotExist: return False def set_email_verified(self, user, value=True): """Set both the UserMetadata and remote (if any) email_verified flags.""" auth_source = self.get_user_auth_source(user) if self.provider == 'LOCAL': if auth_source != UserAuthSource.LOCAL: raise AuthError elif self.provider == 'KEYCLOAK': if auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=self.get_user_remote_uid(user), payload={'email': user.email, 'emailVerified': value}) else: raise AuthError self._set_email_verified(user, value) def _set_email_verified(self, user, value=True): """Set the UserMetadata email_verified flag. """ value = '1' if value else '0' try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="email_verified", value=value) def validate_password(self, user, password): if user.crypto == UserCrypto.BCRYPT: thash = bcrypt.hashpw(password.encode('utf-8'), user.password.encode('utf-8')) if thash == user.password.encode('utf-8'): if self.provider == 'KEYCLOAK': kuid = self.keycloak_admin.create_user({'email': user.email, 'username': user.name, 'enabled': True, 'emailVerified': self.is_email_verified(user), 'credentials': [{'value': password, 'type': 'password'}]}) self.set_user_remote_uid(user, kuid) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = '' user.save() return True elif (user.crypto == UserCrypto.REMOTE and self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == 'KEYCLOAK'): try: # TODO do something with the token self.keycloak_openid.token(username=user.name, password=password) return True except KeycloakError as err: return False return False def change_user_status(self, user, new_status): if new_status == 10: payload = {'email': '', 'emailVerified': False, 'enabled': False} user.email = '' user.email_verified = False self.clear_pending_email(user) elif user.status != 10 and new_status == 5: if user.email and self.is_email_verified(user): domain = user.email.split('@')[1] logger.info('Banned %s; confirmed email on %s', user.name, domain) payload = {'enabled': False} elif user.status != 10 and new_status == 0: payload = {'enabled': True} else: raise RuntimeError("Invalid user status") if (new_status == 0 and email_validation_is_required() and not self.is_email_verified(user)): new_status = 1 if user.crypto == UserCrypto.REMOTE: if (self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == 'KEYCLOAK'): self.keycloak_admin.update_user(user_id=self.get_user_remote_uid(user), payload=payload) user.status = new_status user.save() User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() def actually_delete_user(self, user): # Used by automatic tests to clean up test realm on server. # You should probably be using change_user_status. if user.crypto == UserCrypto.REMOTE: self.keycloak_admin.delete_user(self.get_user_remote_uid(user))
class AuthProvider: def __init__(self, app=None): if app is not None: self.init_app(app) def init_app(self, app): self.provider = app.config["THROAT_CONFIG"].auth.provider if self.provider == "KEYCLOAK": cfg = app.config["THROAT_CONFIG"].auth.keycloak self.keycloak_admin = KeycloakAdmin( server_url=cfg.server, realm_name=cfg.user_realm, user_realm_name=cfg.admin_realm, client_id=cfg.admin_client, client_secret_key=cfg.admin_secret, verify=True, auto_refresh_token=["get", "put", "post", "delete"], ) self.keycloak_openid = KeycloakOpenID( server_url=cfg.server, client_id=cfg.auth_client, client_secret_key=cfg.auth_secret, realm_name=cfg.user_realm, verify=True, ) def get_user_by_email(self, email): try: return User.get(fn.Lower(User.email) == email.lower()) except User.DoesNotExist: try: um = UserMetadata.get( (UserMetadata.key == "pending_email") & (fn.Lower(UserMetadata.value) == email.lower())) return User.get(User.uid == um.uid) except UserMetadata.DoesNotExist: pass if self.provider == "KEYCLOAK": users = self.keycloak_admin.get_users({"email": email}) for userdict in users: try: return User.get(User.name == userdict["username"]) except User.DoesNotExist: return None return None def create_user(self, name, password, email, status=UserStatus.OK, verified_email=False): auth_source = getattr(UserAuthSource, self.provider) if self.provider == "LOCAL": uid = str(uuid.uuid4()) crypto = UserCrypto.BCRYPT password = bcrypt.hashpw(password.encode("utf-8"), bcrypt.gensalt()) else: uid = self.keycloak_admin.create_user({ "email": email, "username": name, "enabled": True, "emailVerified": verified_email, "credentials": [{ "value": password, "type": "password" }], }) password = "" crypto = UserCrypto.REMOTE user = User.create( uid=uid, name=name, crypto=crypto, password=password, status=status, email=email, joindate=datetime.utcnow(), ) self.set_user_auth_source(user, auth_source) self._set_email_verified(user, verified_email) return user @staticmethod def get_user_auth_source(user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) return UserAuthSource(int(umd.value)) except UserMetadata.DoesNotExist: return UserAuthSource.LOCAL @staticmethod def set_user_auth_source(user, value): value = str(int(value)) try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="auth_source", value=value) @staticmethod def get_user_remote_uid(user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "remote_uid")) return umd.value except UserMetadata.DoesNotExist: return user.uid @staticmethod def set_user_remote_uid(user, remote_uid): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "remote_uid")) umd.value = remote_uid umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="remote_uid", value=remote_uid) pass def change_password(self, user, old_password, new_password): if self.validate_password(user, old_password): auth_source = self.get_user_auth_source(user) if self.provider == "LOCAL" and auth_source == UserAuthSource.LOCAL: user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode("utf-8"), bcrypt.gensalt()) user.save() elif self.provider == "KEYCLOAK" and auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload={ "credentials": [{ "value": new_password, "type": "password" }] }, ) else: raise AuthError # Invalidate other existing login sessions. User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() theuser = misc.load_user(user.uid) login_user(theuser, remember=session.get("remember_me", False)) def reset_password(self, user, new_password): auth_source = self.get_user_auth_source(user) if self.provider == "LOCAL": if auth_source == UserAuthSource.LOCAL: user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode("utf-8"), bcrypt.gensalt()) else: raise AuthError elif self.provider == "KEYCLOAK": if auth_source == UserAuthSource.LOCAL: kuid = self.keycloak_admin.create_user({ "email": user.email, "username": user.name, "enabled": True, "emailVerified": self.is_email_verified(user), "credentials": [{ "value": new_password, "type": "password" }], }) self.set_user_remote_uid(user, kuid) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = "" elif auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload={ "credentials": [{ "value": new_password, "type": "password" }] }, ) else: raise AuthError user.save() User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() def change_unconfirmed_user_email(self, user, new_email): """Use this to change the email before the user confirms it. To change the email of a user with a confirmed email address, use the pending email change functions below. """ user.email = new_email if self.provider == "KEYCLOAK": self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload={ "email": new_email, "emailVerified": False }, ) user.save() User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() @staticmethod def get_pending_email(user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) return umd.value except UserMetadata.DoesNotExist: return None @staticmethod def clear_pending_email(user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.delete_instance() except UserMetadata.DoesNotExist: return None @staticmethod def set_pending_email(user, email): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.value = email umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="pending_email", value=email) def confirm_pending_email(self, user, email): umd = None try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) except UserMetadata.DoesNotExist: pass auth_source = self.get_user_auth_source(user) if self.provider == "LOCAL": if auth_source != UserAuthSource.LOCAL: raise AuthError elif self.provider == "KEYCLOAK": if auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload={ "email": email, "emailVerified": True }, ) else: raise AuthError if umd is not None: umd.delete_instance() user.email = email user.save() self._set_email_verified(user) @staticmethod def is_email_verified(user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) return umd.value == "1" except UserMetadata.DoesNotExist: return False def set_email_verified(self, user, value=True): """Set both the UserMetadata and remote (if any) email_verified flags.""" auth_source = self.get_user_auth_source(user) if self.provider == "LOCAL": if auth_source != UserAuthSource.LOCAL: raise AuthError elif self.provider == "KEYCLOAK": if auth_source == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload={ "email": user.email, "emailVerified": value }, ) else: raise AuthError self._set_email_verified(user, value) @staticmethod def _set_email_verified(user, value=True): """Set the UserMetadata email_verified flag.""" value = "1" if value else "0" try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="email_verified", value=value) def validate_password(self, user, password): if user.crypto == UserCrypto.BCRYPT: thash = bcrypt.hashpw(password.encode("utf-8"), user.password.encode("utf-8")) if thash == user.password.encode("utf-8"): if self.provider == "KEYCLOAK": kuid = self.keycloak_admin.create_user({ "email": user.email, "username": user.name, "enabled": True, "emailVerified": self.is_email_verified(user), "credentials": [{ "value": password, "type": "password" }], }) self.set_user_remote_uid(user, kuid) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = "" user.save() return True elif (user.crypto == UserCrypto.REMOTE and self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == "KEYCLOAK"): try: # TODO do something with the token self.keycloak_openid.token(username=user.name, password=password) return True except KeycloakError: return False return False def change_user_status(self, user, new_status): if new_status == 10: payload = {"email": "", "emailVerified": False, "enabled": False} user.email = "" user.email_verified = False self.clear_pending_email(user) elif user.status != 10 and new_status == 5: if user.email and self.is_email_verified(user): domain = user.email.split("@")[1] logger.info("Banned %s; confirmed email on %s", user.name, domain) payload = {"enabled": False} elif user.status != 10 and new_status == 0: payload = {"enabled": True} else: raise RuntimeError("Invalid user status") if (new_status == 0 and email_validation_is_required() and not self.is_email_verified(user)): new_status = 1 if user.crypto == UserCrypto.REMOTE: if (self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == "KEYCLOAK"): self.keycloak_admin.update_user( user_id=self.get_user_remote_uid(user), payload=payload) user.status = new_status user.save() User.update(resets=User.resets + 1).where(User.uid == user.uid).execute() def actually_delete_user(self, user): # Used by automatic tests to clean up test realm on server. # You should probably be using change_user_status. if user.crypto == UserCrypto.REMOTE: self.keycloak_admin.delete_user(self.get_user_remote_uid(user))
class AuthProvider: def __init__(self, app=None): if app is not None: self.init_app(app) def init_app(self, app): self.provider = app.config['THROAT_CONFIG'].auth.provider if self.provider == 'KEYCLOAK': cfg = app.config['THROAT_CONFIG'].auth.keycloak self.keycloak_admin = KeycloakAdmin( server_url=cfg.server, realm_name=cfg.user_realm, user_realm_name=cfg.admin_realm, client_id=cfg.admin_client, client_secret_key=cfg.admin_secret, verify=True, auto_refresh_token=['get', 'put', 'post', 'delete']) self.keycloak_openid = KeycloakOpenID( server_url=cfg.server, client_id=cfg.auth_client, client_secret_key=cfg.auth_secret, realm_name=cfg.user_realm, verify=True) def get_user_by_email(self, email): # TODO when there are pending change emails, check those too. try: return User.get(User.email == email) except User.DoesNotExist: try: um = UserMetadata.get((UserMetadata.key == 'pending_email') & (UserMetadata.value == email)) return User.get(User.uid == um.uid) except UserMetadata.DoesNotExist: pass if self.provider == 'KEYCLOAK': users = self.keycloak_admin.get_users({"email": email}) for userdict in users: try: return User.get(User.name == userdict['username']) except User.DoesNotExist: return None return None def create_user(self, name, password, email, verified_email=False): auth_source = getattr(UserAuthSource, self.provider) if self.provider == 'LOCAL': uid = str(uuid.uuid4()) crypto = UserCrypto.BCRYPT password = bcrypt.hashpw(password.encode('utf-8'), bcrypt.gensalt()) else: uid = self.keycloak_admin.create_user({ 'email': email, 'username': name, 'enabled': True, 'emailVerified': verified_email, 'credentials': [{ 'value': password, 'type': 'password' }] }) # email = '' # Store emails in local db, or not? password = '' crypto = UserCrypto.REMOTE user = User.create(uid=uid, name=name, crypto=crypto, password=password, email=email, joindate=datetime.utcnow()) self.set_user_auth_source(user, auth_source) self.set_email_verified(user, verified_email) return user def get_user_auth_source(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) return UserAuthSource(int(umd.value)) except UserMetadata.DoesNotExist: return UserAuthSource.LOCAL def set_user_auth_source(self, user, value): value = str(int(value)) try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "auth_source")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="auth_source", value=value) def change_password(self, user, old_password, new_password): if not self.validate_password(user, old_password): return False if self.provider == 'LOCAL': user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) elif self.provider == 'KEYCLOAK': # validate_password promotes LOCAL users to KEYCLOAK ones. self.keycloak_admin.update_user(user_id=user.uid, payload={ 'credentials': [{ 'value': new_password, 'type': 'password' }] }) # Invalidate other existing login sessions. user.resets += 1 user.save() theuser = misc.load_user(user.uid) login_user(theuser, remember=session.get("remember_me", False)) def reset_password(self, user, new_password): if self.provider == 'LOCAL': user.crypto = UserCrypto.BCRYPT user.password = bcrypt.hashpw(new_password.encode('utf-8'), bcrypt.gensalt()) self.set_user_auth_source(user, UserAuthSource.LOCAL) elif self.provider == 'KEYCLOAK': if self.get_user_auth_source(user) == UserAuthSource.LOCAL: self.keycloak_admin.create_user({ 'id': user.uid, 'email': user.email, 'username': user.name, 'enabled': True, 'emailVerified': self.is_email_verified(user), 'credentials': [{ 'value': new_password, 'type': 'password' }] }) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = '' else: self.keycloak_admin.update_user(user_id=user.uid, payload={ 'credentials': [{ 'value': new_password, 'type': 'password' }] }) user.resets += 1 user.save() def get_pending_email(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) return umd.value except UserMetadata.DoesNotExist: return None def clear_pending_email(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.delete_instance() except UserMetadata.DoesNotExist: return None def set_pending_email(self, user, email): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.value = email umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="pending_email", value=email) def confirm_pending_email(self, user, email): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "pending_email")) umd.delete_instance() except UserMetadata.DoesNotExist: pass if self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=user.uid, payload={'email': email}) user.email = email user.save() self.set_email_verified(user) def is_email_verified(self, user): try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) return bool(umd.value) except UserMetadata.DoesNotExist: return False def set_email_verified(self, user, value=True): if self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK: self.keycloak_admin.update_user(user_id=user.uid, payload={'emailVerified': value}) value = '1' if value else '0' try: umd = UserMetadata.get((UserMetadata.uid == user.uid) & (UserMetadata.key == "email_verified")) umd.value = value umd.save() except UserMetadata.DoesNotExist: UserMetadata.create(uid=user.uid, key="email_verified", value=value) def validate_password(self, user, password): if user.crypto == UserCrypto.BCRYPT: thash = bcrypt.hashpw(password.encode('utf-8'), user.password.encode('utf-8')) if thash == user.password.encode('utf-8'): if self.provider == 'KEYCLOAK': self.keycloak_admin.create_user({ 'id': user.uid, 'email': user.email, 'username': user.name, 'enabled': True, 'emailVerified': self.is_email_verified(user), 'credentials': [{ 'value': password, 'type': 'password' }] }) self.set_user_auth_source(user, UserAuthSource.KEYCLOAK) user.crypto = UserCrypto.REMOTE user.password = '' user.save() return True elif (user.crypto == UserCrypto.REMOTE and self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == 'KEYCLOAK'): try: # TODO do something with the token self.keycloak_openid.token(username=user.name, password=password) return True except KeycloakError as err: return False return False def change_user_status(self, user, new_status): if new_status == 10: payload = {'email': '', 'emailVerified': False, 'enabled': False} user.email = '' user.email_verified = False elif user.status != 10 and new_status == 5: payload = {'enabled': False} elif user.status != 10 and new_status == 0: payload = {'enabled': True} else: raise RuntimeError("Invalid user status") if user.crypto == UserCrypto.REMOTE: if (self.get_user_auth_source(user) == UserAuthSource.KEYCLOAK and self.provider == 'KEYCLOAK'): self.keycloak_admin.update_user(user_id=user.uid, payload=payload) user.status = new_status user.resets += 1 # Make them log in again. user.save() def actually_delete_user(self, user): # Used by automatic tests to clean up test realm on server. # You should probably be using mark_user_deleted. if user.crypto == UserCrypto.REMOTE: self.keycloak_admin.delete_user(user.uid)
class KeycloakUserManagement(UserManagement): def __init__(self, ex, parameters=None): from keycloak import KeycloakOpenID if parameters is None: parameters = {} self.ex = ex self.server_url = parameters["server_url"] self.client_id = parameters["client_id"] self.realm_name = parameters["realm_name"] self.client_secret_key = parameters["client_secret_key"] self.keycloak_manager = KeycloakOpenID( server_url=self.server_url, client_id=self.client_id, realm_name=self.realm_name, client_secret_key=self.client_secret_key) UserManagement.__init__(self, ex) def do_login(self, user, password): """ Logs in a user and returns a session id Parameters ------------ user Username password Password Returns ------------ session_id Session ID """ try: token = self.keycloak_manager.token(user, password) return token['access_token'] except: # traceback.print_exc() pass return None def check_session_validity(self, session_id): """ Checks the validity of a session Parameters ------------ session_id Session ID Returns ------------ boolean Boolean value """ validity = False try: if not (str(session_id) == "null"): userinfo = self.keycloak_manager.userinfo(session_id) if type(userinfo["preferred_username"]) is str: if userinfo["preferred_username"]: validity = True except: # traceback.print_exc() pass return validity def get_user_from_session(self, session_id): """ Gets the user from the session Parameters ------------ session_id Session ID Returns ------------ user User ID """ user = None try: if not (str(session_id) == "null"): userinfo = self.keycloak_manager.userinfo(session_id) if type(userinfo["preferred_username"]) is str: if userinfo["preferred_username"]: user = userinfo["preferred_username"] except: # traceback.print_exc() pass return user def clean_expired_sessions(self): """ Cleans the expired sessions in IAM """ pass def get_all_sessions(self): """ Gets all sessions from the users database Returns ----------- sessions List of sessions """ return None def get_all_users(self): """ Possibly get all the users from the users database Returns ------------- users List of users """ return []
from keycloak import KeycloakOpenID import pprint keycloak_openid = KeycloakOpenID(server_url="https://localhost:8181/auth/", client_id="example_client", realm_name="example_realm", client_secret_key="0f0f6195-41bb-4077-bd30-469964328ba0", verify=False) config_well_know = keycloak_openid.well_know() token = keycloak_openid.token("example_client", "0f0f6195-41bb-4077-bd30-469964328ba0", grant_type="client_credentials") pprint.pprint(token)
config_well_know = keycloak_openid.well_know() except KeycloakError, e: pam_syslog( syslog.LOG_NOTICE, pamh, "auth", "unable to authenticate for %s: %d %s" % (user, e.response_code, e.error_message)) return pamh.PAM_AUTHINFO_UNAVAIL if pamh.authtok is None: passmsg = pamh.Message(pamh.PAM_PROMPT_ECHO_OFF, "Password: "******"auth", "authentication failure for %s: %d %s" % (user, e.response_code, e.error_message)) if e.response_code == 401: return pamh.PAM_AUTH_ERR
def check_password(self, user_id, password): """ Attempt to authenticate a user against an Keycloak Server and register an account if none exists. Returns: True if authentication against Keycloak was successful """ if not password: defer.returnValue(False) localpart = user_id.split(":", 1)[0][1:] logger.info("! %s", localpart) keycloak_openid = KeycloakOpenID(server_url=self.url, client_id=self.client_id, realm_name=self.realm_name, client_secret_key=self.secret_key) logger.debug("Attempting Keycloak connection with %s", self.url) try: token = yield keycloak_openid.token(localpart, password) except KeycloakAuthenticationError as e: logger.info("Failed login attempt %s error: %s", localpart, e) defer.returnValue(False) logger.info("User %s authenticated", user_id) options = {"verify_signature": True, "verify_aud": True, "exp": True} key = self.public_key if self.algorithm == 'RS256': key = '-----BEGIN PUBLIC KEY-----\n' + key + '\n-----END PUBLIC KEY-----' token_info = keycloak_openid.decode_token(token['access_token'], key=key, algorithms=[self.algorithm], options=options) if not (yield self.account_handler.check_user_exists(user_id)): logger.info("User %s does not exist yet, creating...", user_id) if localpart != localpart.lower() and self.regLower: logger.info( 'User %s was cannot be created due to username lowercase policy', localpart) defer.returnValue(False) user_id, access_token = (yield self.account_handler.register( localpart=localpart)) registration = True logger.info( "Registration based on REST data was successful for %s", user_id) else: logger.info("User %s already exists, registration skipped", user_id) if bool(self.profile_attrs): logger.info("profile attrs") store = yield self.account_handler.hs.get_profile_handler().store profile = {} for key, alias in self.profile_attrs.items(): if alias in token_info: profile[key] = token_info[alias] display_name = profile.pop('display_name', None) if display_name: logger.info( "Setting display name to '%s' based on profile data", display_name) yield store.set_profile_displayname(localpart, display_name) logger.info("end profile attrs") # TODO 3pids else: logger.info("No profile data") def _save_keycloak_token(txn): template = """ INSERT INTO keycloak_provider_tokens (user_id, refresh_tokens) VALUES ('{0}', ARRAY['{1}']) ON CONFLICT (user_id) DO UPDATE SET refresh_tokens = array_append(keycloak_provider_tokens.refresh_tokens, '{1}') """ sql = template.format(user_id, token['refresh_token']) txn.execute(sql) self.account_handler.run_db_interaction("save_keycloak_token", _save_keycloak_token) logger.info("insert end") defer.returnValue(True)
class Keycloakconnector: keycloak_openid = None def __init__(self, serverurl, realm, clientid, secret): self.keycloak_openid = KeycloakOpenID(server_url=serverurl, client_id=clientid, realm_name=realm, client_secret_key=secret, verify=True) config_well_know = self.keycloak_openid.well_know() self.serverurl = serverurl self.realm = realm # print(config_well_know) def getToken(self, appID, api): token = self.keycloak_openid.token(username="", password="", grant_type=["client_credentials"]) token = token['access_token'] print("Access token = ") print(token) return token # Get JWT public key. def getJWTPublickey(self): cert = self.keycloak_openid.certs() if cert is None: return "" x5c = cert.get('keys')[0]['x5c'][0] x5c = '-----BEGIN CERTIFICATE-----\n' + x5c + '\n-----END CERTIFICATE-----' x509 = OpenSSL.crypto.load_certificate(FILETYPE_PEM, x5c) pubkey = x509.get_pubkey() pubkey = OpenSSL.crypto.dump_publickey(FILETYPE_PEM, pubkey).decode("utf-8") return pubkey def getClientRoles(self, clientid, daa_token): api_call_headers = { 'Authorization': 'Bearer ' + daa_token, 'cache-control': "no-cache", "Content-Type": "application/json", "charset": "utf-8" } roles = "" try: # get keycloak client representation (needed to get the value of id_of_client parameter) client_url = self.serverurl + "admin/realms/" + self.realm + "/clients?clientId=" + clientid client_rep = get(client_url, headers=api_call_headers) client_json = json.loads(client_rep.text) id_of_client = client_json[0]["id"] print(id_of_client) # get client roles roles_url = self.serverurl + "admin/realms/" + self.realm + "/clients/" + id_of_client + "/roles" roles_rep = get(roles_url, headers=api_call_headers) keycloak_roles = json.loads(roles_rep.text) if "can_id_read" in keycloak_roles[0]: can_id_read_array = keycloak_roles[0]["description"].split(",") can_id_write_array = keycloak_roles[1]["description"].split( ",") else: can_id_read_array = keycloak_roles[1]["description"].split(",") can_id_write_array = keycloak_roles[0]["description"].split( ",") roles = json.loads('{"can_id_read": "", "can_id_write" : ""}') roles["can_id_read"] = can_id_read_array roles["can_id_write"] = can_id_write_array except JSONDecodeError as error: print( "Json decoding error occurred. Input is not in json format or does not contain the required fields" ) raise SyntaxError except Exception as exp: print("An exception occurred while retrieving client roles {}". format(exp)) return roles
class KeycloakClient: """Wrapper for Keycloak OpenID and admin clients.""" def __init__(self, server, resource, realm, secret, admin_user, admin_pass): self.openid = KeycloakOpenID( server_url=server, client_id=resource, realm_name=realm, client_secret_key=secret, ) self.admin = _KeycloakAdmin( server_url=server, client_id=resource, realm_name=realm, client_secret_key=secret, username=admin_user, password=admin_pass, auto_refresh_token=['get', 'put', 'post', 'delete'], ) def login(self, username, password): return self.openid.token(username, password, totp=None) def token_refresh(self, refresh_token): return self.openid.refresh_token(refresh_token) def token_info(self, token): return self.openid.introspect(token) def user_list(self, query=None): return self.admin.get_users(query) def user_get(self, user_id): return self.admin.get_user(user_id) def user_create(self, data): data = data.copy() data.setdefault('enabled', True) if 'password' in data: data['credentials'] = [ { 'type': 'password', 'value': data['password'] }, ] del data['password'] return self.admin.create_user(data) def user_update(self, user_id, data): data = data.copy() if 'password' in data: data['credentials'] = [ { 'type': 'password', 'value': data['password'] }, ] del data['password'] self.admin.update_user(user_id, data) def user_delete(self, user_id): self.admin.delete_user(user_id) def user_group_list(self, user_id): """Get list of groups user belongs to.""" return self.admin.get_user_groups(user_id) def user_role_list(self, user_id): """ Get set of all user roles (**role names**), directly assigned and also inherited from a group. """ roles = set() # directly assigned roles roles = { i['name'] for i in self.admin.get_realm_roles_of_user(user_id) } # iherited roles from group for group in self.user_group_list(user_id): roles |= { i['name'] for i in self.admin.get_group_realm_roles(group['id']) } return roles def user_check_group(self, user_id, group_id): """Check if user belongs to the given group.""" return self.user_check_group_any(user_id, [group_id]) def user_check_group_any(self, user_id, group_id_list): """Check if user belongs to any of the given groups.""" return any(group['id'] in group_id_list for group in self.user_group_list(user_id)) def user_check_role(self, user_id, role_name): """Check if user has role.""" return role_name in self.user_role_list(user_id) def group_list(self): return self.admin.get_groups() def group_get(self, group_id): return self.admin.get_group(group_id) def group_create(self, data): # `create_group` always returns b'' self.admin.create_group(data) for group in self.group_list(): if group['name'] == data['name']: return group['id'] def group_update(self, group_id, data): self.admin.update_group(group_id, data) def group_delete(self, group_id): self.admin.delete_group(group_id) def group_user_list(self, group_id): """Get list of users in group.""" return self.admin.get_group_members(group_id) def group_user_add(self, user_id, group_id): """Add user to group.""" self.admin.group_user_add(user_id, group_id) def group_user_remove(self, user_id, group_id): """Remove user from group.""" self.admin.group_user_remove(user_id, group_id) def group_role_list(self, group_id): return self.admin.get_group_realm_roles(group_id) def group_role_add(self, role_name, group_id): """Add role to group. **Role NAME, not ID.**""" role = self.role_get(role_name) self.admin.assign_group_realm_roles(group_id, [role]) def group_role_remove(self, role_name, group_id): """Remove role from group. **Role NAME, not ID.**""" role = self.role_get(role_name) self.admin.delete_group_realm_roles(group_id, [role]) def role_list(self): return self.admin.get_realm_roles() def role_get(self, role_name): """Get role by name. **NAME, not ID.**""" return self.admin.get_realm_role(role_name) def role_create(self, data): # `create_role` always returns b'' self.admin.create_realm_role(data) for role in self.role_list(): if role['name'] == data['name']: return role['name'] def role_update(self, role_id, data): self.admin.update_realm_role(role_id, data) def role_delete(self, role_id): self.admin.delete_realm_role(role_id)
from keycloak import KeycloakOpenID # Configure client keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/", client_id="example_client", realm_name="example_realm", client_secret_key="secret") # Get WellKnow config_well_know = keycloak_openid.well_know() # Get Token token = keycloak_openid.token("user", "password") token = keycloak_openid.token("user", "password", totp="012345") # Get Userinfo userinfo = keycloak_openid.userinfo(token['access_token']) # Refresh token token = keycloak_openid.refresh_token(token['refresh_token']) # Logout keycloak_openid.logout(token['refresh_token']) # Get Certs certs = keycloak_openid.certs() # Get RPT (Entitlement) token = keycloak_openid.token("user", "password") rpt = keycloak_openid.entitlement(token['access_token'], "resource_id")