def authenticate(self): auth_header = self.request.headers.get('Authorization').split(' ') if len(auth_header) != 2: return False bearer, token = auth_header conf = dci_config.generate_conf() try: decoded_token = auth.decode_jwt(token, conf['SSO_PUBLIC_KEY'], conf['SSO_CLIENT_ID']) except jwt_exc.DecodeError: raise dci_exc.DCIException('Invalid JWT token.', status_code=401) except jwt_exc.ExpiredSignatureError: raise dci_exc.DCIException('JWT token expired, please refresh.', status_code=401) team_id = None ro_group = dci_config.generate_conf().get('SSO_READ_ONLY_GROUP') realm_access = decoded_token['realm_access'] if 'roles' in realm_access and ro_group in realm_access['roles']: team_id = flask.g.team_redhat_id user_info = self._get_user_info(decoded_token, team_id) try: self.identity = self._get_or_create_user(user_info) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(models.USERS.name, 'username') return True
def verify_and_get_components_ids(topic_id, components_ids, component_types, db_conn=None): """Process some verifications of the provided components ids.""" db_conn = db_conn or flask.g.db_conn if len(components_ids) != len(component_types): msg = 'The number of component ids does not match the number ' \ 'of component types %s' % component_types raise dci_exc.DCIException(msg, status_code=412) # get the components from their ids schedule_component_types = set() for c_id in components_ids: where_clause = sql.and_(models.COMPONENTS.c.id == c_id, models.COMPONENTS.c.topic_id == topic_id, models.COMPONENTS.c.state == 'active') query = (sql.select([models.COMPONENTS]) .where(where_clause)) cmpt = db_conn.execute(query).fetchone() if cmpt is None: msg = 'Component id %s not found or not exported' % c_id raise dci_exc.DCIException(msg, status_code=412) cmpt = dict(cmpt) if cmpt['type'] in schedule_component_types: msg = ('Component types malformed: type %s duplicated.' % cmpt['type']) raise dci_exc.DCIException(msg, status_code=412) schedule_component_types.add(cmpt['type']) return components_ids
def _get_components_from_ids(topic_id, rconfiguration, components_ids, component_types): # used for error message source = ' topic %s' % topic_id if (rconfiguration is not None and rconfiguration['component_types'] != []): source = ' rconfiguration %s' % rconfiguration['id'] if len(components_ids) != len(component_types): msg = 'The number of component ids does not match the number ' \ 'of component types of %s' % source raise dci_exc.DCIException(msg, status_code=412) # get the components from their ids schedule_component_types = set() for c_id in components_ids: where_clause = sql.and_(models.COMPONENTS.c.id == c_id, models.COMPONENTS.c.topic_id == topic_id, models.COMPONENTS.c.export_control == True, # noqa models.COMPONENTS.c.state == 'active') query = (sql.select([models.COMPONENTS]) .where(where_clause)) cmpt = flask.g.db_conn.execute(query).fetchone() if cmpt is None: msg = 'Component id %s not found or not exported' % c_id raise dci_exc.DCIException(msg, status_code=412) cmpt = dict(cmpt) if cmpt['type'] in schedule_component_types: msg = ('Component types malformed: type %s duplicated.' % cmpt['type']) raise dci_exc.DCIException(msg, status_code=412) schedule_component_types.add(cmpt['type']) return components_ids
def decode_token_with_latest_public_key(token): """Get the latest public key and decode the JWT token with it. This function is usefull when the SSO server rotated its key.""" conf = dci_config.CONFIG try: latest_public_key = get_latest_public_key() except Exception as e: raise dci_exc.DCIException( 'Unable to get last SSO public key: %s' % str(e), # noqa status_code=401) # SSO server didn't update its public key if conf['SSO_PUBLIC_KEY'] == latest_public_key: raise dci_exc.DCIException('Invalid JWT token.', status_code=401) # noqa try: decoded_token = auth.decode_jwt(token, latest_public_key, conf['SSO_CLIENT_ID']) conf['SSO_PUBLIC_KEY'] = latest_public_key return decoded_token except (jwt_exc.DecodeError, TypeError): raise dci_exc.DCIException('Invalid JWT token.', status_code=401) # noqa except jwt_exc.ExpiredSignatureError: raise dci_exc.DCIException( 'JWT token expired, please refresh.', # noqa status_code=401)
def where_query(where, table, columns): where_conds = [] err_msg = 'Invalid where key: "%s"' for where_elem in where: try: name, value = where_elem.split(':', 1) except ValueError: payload = { 'error': 'where key must have the following form ' '"key:value"' } raise dci_exc.DCIException(err_msg % where_elem, payload=payload) if name not in columns: payload = {'valid_keys': list(columns.keys())} raise dci_exc.DCIException(err_msg % name, payload=payload) m_column = getattr(table.c, name) # TODO(yassine): do the same for columns type different from string # if it's an Integer column, then try to cast the value if str(m_column.type) == "UUID": pass elif m_column.type.python_type == int: try: value = int(value) except ValueError: payload = {name: 'not integer'} raise dci_exc.DCIException(err_msg % name, payload=payload) where_conds.append(m_column == value) return where_conds
def get_client_info(self): """Extracts timestamp, client type and client id from a DCI-Client-Info header. Returns a hash with the three values. Throws an exception if the format is bad or if strptime fails.""" if 'DCI-Client-Info' not in self.request.headers: raise dci_exc.DCIException('Header DCI-Client-Info missing', status_code=401) client_info = self.request.headers.get('DCI-Client-Info') client_info = client_info.split('/') if len(client_info) != 3 or not all(client_info): raise dci_exc.DCIException( 'DCI-Client-Info should match the following format: ' + '"YYYY-MM-DD HH:MI:SSZ/<client_type>/<id>"') dateformat = '%Y-%m-%d %H:%M:%SZ' try: timestamp = datetime.strptime(client_info[0], dateformat) return { 'timestamp': timestamp, 'type': client_info[1], 'id': client_info[2], } except ValueError: raise dci_exc.DCIException('Bad date format in DCI-Client-Info', '401')
def where_query(where, table, columns): where_conds = [] err_msg = 'Invalid where key: "%s"' def _get_column(table, columns, name): payload = { 'error': 'where key must have the following form ' '"key:value"' } if '.' in name: subtable_name, name = name.split('.') table_obj = embeds.EMBED_STRING_TO_OBJECT[table.name] try: table = table_obj[subtable_name] except KeyError: payload = {'valid_keys': list(table_obj.keys())} raise dci_exc.DCIException(err_msg % name, payload=payload) columns = get_columns_name_with_objects(table) if name not in columns: payload = {'valid_keys': list(columns.keys())} raise dci_exc.DCIException(err_msg % name, payload=payload) return getattr(table.c, name) array_columns_to_list = {} for where_elem in where: try: name, value = where_elem.split(':', 1) except ValueError: payload = { 'error': 'where key must have the following form ' '"key:value"' } raise dci_exc.DCIException(err_msg % where_elem, payload=payload) m_column = _get_column(table, columns, name) # TODO(yassine): do the same for columns type different from string # if it's an Integer column, then try to cast the value try: if str(m_column.type) == "UUID" and uuid.UUID(value): where_conds.append(m_column == value) elif m_column.type.python_type == int: where_conds.append(m_column == int(value)) elif m_column.type.python_type == list: if m_column not in array_columns_to_list: array_columns_to_list[m_column] = [value] else: array_columns_to_list[m_column] += [value] else: where_conds.append(m_column == value) except ValueError: payload = {name: '%s is not a %s' % (name, m_column.type)} raise dci_exc.DCIException(err_msg % name, payload=payload) for col, values in array_columns_to_list.items(): where_conds.append(col.contains(values)) return where_conds
def create_files(user): file_info = get_file_info_from_headers(dict(flask.request.headers)) values = dict.fromkeys( ['md5', 'mime', 'jobstate_id', 'job_id', 'name', 'test_id']) values.update(file_info) if values.get('jobstate_id') is None and values.get('job_id') is None: raise dci_exc.DCIException('HTTP headers DCI-JOBSTATE-ID or ' 'DCI-JOB-ID must be specified') if values.get('name') is None: raise dci_exc.DCIException('HTTP header DCI-NAME must be specified') if values.get('jobstate_id') and values.get('job_id') is None: jobstate = v1_utils.verify_existence_and_get(values.get('jobstate_id'), models.JOBSTATES) values['job_id'] = jobstate['job_id'] job = v1_utils.verify_existence_and_get(values.get('job_id'), models.JOBS) if (user.is_not_in_team(job['team_id']) and user.is_read_only_user() and user.is_not_epm()): raise dci_exc.Unauthorized() file_id = utils.gen_uuid() file_path = files_utils.build_file_path(job['team_id'], values['job_id'], file_id) content = files_utils.get_stream_or_content_from_request(flask.request) store = dci_config.get_store('files') store.upload(file_path, content) s_file = store.head(file_path) etag = utils.gen_etag() values.update({ 'id': file_id, 'created_at': datetime.datetime.utcnow().isoformat(), 'updated_at': datetime.datetime.utcnow().isoformat(), 'team_id': job['team_id'], 'md5': None, 'size': s_file['content-length'], 'state': 'active', 'etag': etag, }) with flask.g.db_conn.begin(): q_insert_file = _TABLE.insert().values(**values) flask.g.db_conn.execute(q_insert_file) result = json.dumps({'file': values}) if values['mime'] == 'application/junit': _, junit_file = store.get(file_path) _process_junit_file(values, junit_file, job) return flask.Response(result, 201, content_type='application/json')
def authenticate(self): auth = self.request.authorization if not auth: raise dci_exc.DCIException('Authorization header missing', status_code=401) user, is_authenticated = \ self.get_user_and_check_auth(auth.username, auth.password) if not is_authenticated: raise dci_exc.DCIException('Invalid user credentials', status_code=401) self.identity = user return True
def _get_auth_class_from_headers(headers): if 'Authorization' not in headers: raise dci_exc.DCIException('Authorization header missing', status_code=401) auth_type = headers.get('Authorization').split(' ')[0] if auth_type == 'Bearer': return am.OpenIDCAuth elif auth_type == 'DCI-HMAC-SHA256': return am.HmacMechanism elif auth_type == 'Basic': return am.BasicAuthMechanism raise dci_exc.DCIException('Authorization scheme %s unknown' % auth_type, status_code=401)
def verify_existence_and_get(id, table, name=None, get_id=False): """Verify the existence of a resource in the database and then return it if it exists, according to the condition, or raise an exception. :param id: id of the resource :param table: the table object :param name: the name of the row to look for :param get_id: if True, return only the ID :return: """ where_clause = table.c.id == id if name: where_clause = table.c.name == name if 'state' in table.columns: where_clause = sql.and_(table.c.state != 'archived', where_clause) query = sql.select([table]).where(where_clause) result = flask.g.db_conn.execute(query).fetchone() if result is None: raise dci_exc.DCIException('Resource "%s" not found.' % id, status_code=404) if get_id: return result.id return result
def purge_archived_files(user): # get all archived files archived_files = base.get_archived_resources(_TABLE) store = dci_config.get_store('files') # for each file delete it from within a transaction # if the SQL deletion or the Store deletion fail then # rollback the transaction, otherwise commit. for file in archived_files: tx = flask.g.db_conn.begin() try: q_delete_file = _TABLE.delete().where(_TABLE.c.id == file['id']) flask.g.db_conn.execute(q_delete_file) file_path = files_utils.build_file_path(file['team_id'], file['job_id'], file['id']) store.delete(file_path) tx.commit() logger.debug('file %s removed' % file_path) except dci_exc.StoreExceptions as e: if e.status_code == 404: logger.warn('file %s not found in store' % file_path) else: raise e except sa_exc.DBAPIError as e: logger.error('Error while removing file %s, message: %s' % (file_path, str(e))) tx.rollback() raise dci_exc.DCIException(str(e)) return flask.Response(None, 204, content_type='application/json')
def sort_query(sort, root_valid_columns, embeds_valid_columns={}, default='-created_at'): # noqa order_by = [] if not sort and not root_valid_columns: return [] if not sort: sort = [default] valid_columns_keys = list(root_valid_columns.keys()) valid_columns = dict(root_valid_columns) if embeds_valid_columns: valid_columns.update(embeds_valid_columns) embed_valid_columns_keys = [ i.replace('_', '.', 1) for i in list(embeds_valid_columns.keys()) ] valid_columns_keys.extend(embed_valid_columns_keys) for sort_elem in sort: sort_order = (sql.desc if sort_elem.startswith('-') else sql.asc) sort_elem = sort_elem.strip(' -') if sort_elem not in valid_columns_keys: raise dci_exc.DCIException( 'Invalid sort key: "%s"' % sort_elem, payload={'Valid sort keys': sorted(set(valid_columns_keys))}) if '.' in sort_elem: sort_elem = sort_elem.replace('.', '_', 1) order_by.append(sort_order(valid_columns[sort_elem])) return order_by
def _validate_input(values, user): topic_id = values.pop('topic_id') remoteci_id = values.get('remoteci_id') values.update({ 'id': utils.gen_uuid(), 'created_at': datetime.datetime.utcnow().isoformat(), 'updated_at': datetime.datetime.utcnow().isoformat(), 'etag': utils.gen_etag(), 'status': 'new' }) remoteci = v1_utils.verify_existence_and_get(remoteci_id, models.REMOTECIS) v1_utils.verify_existence_and_get(topic_id, models.TOPICS) # let's kill existing running jobs for the remoteci where_clause = sql.expression.and_( _TABLE.c.remoteci_id == remoteci_id, _TABLE.c.status.in_(('new', 'pre-run', 'running', 'post-run')) ) kill_query = _TABLE .update().where(where_clause).values(status='killed') flask.g.db_conn.execute(kill_query) if remoteci['state'] != 'active': message = 'RemoteCI "%s" is disabled.' % remoteci_id raise dci_exc.DCIException(message, status_code=412) # The user belongs to the topic then we can start the scheduling v1_utils.verify_team_in_topic(user, topic_id) return topic_id, remoteci
def create_configuration(user, r_id): values_configuration = v1_utils.common_values_dict(user) values_configuration.update( schemas.rconfiguration.post(flask.request.json)) values_configuration.update(flask.request.json) remoteci = v1_utils.verify_existence_and_get(r_id, _TABLE) if not user.is_in_team(remoteci['team_id']): raise auth.UNAUTHORIZED rconfiguration_id = values_configuration.get('id') with flask.g.db_conn.begin(): try: # insert configuration query = _RCONFIGURATIONS.insert().\ values(**values_configuration) flask.g.db_conn.execute(query) # insert join between rconfiguration and remoteci values_join = { 'rconfiguration_id': rconfiguration_id, 'remoteci_id': r_id} query = models.JOIN_REMOTECIS_RCONFIGURATIONS.insert().\ values(**values_join) flask.g.db_conn.execute(query) except sa_exc.IntegrityError as ie: raise dci_exc.DCIException('Integrity Error: %s' % str(ie)) return flask.Response( json.dumps({'rconfiguration': values_configuration}), 201, headers={'ETag': values_configuration['etag']}, content_type='application/json' )
def put_meta(job_id, meta_id): """Modify a meta.""" # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.meta.put(flask.request.json) meta_retrieved = v1_utils.verify_existence_and_get(meta_id, _TABLE) if meta_retrieved['job_id'] != job_id: raise dci_exc.DCIException( "Meta '%s' is not associated to job '%s'." % (meta_id, job_id)) values['etag'] = utils.gen_etag() where_clause = sql.and_( _TABLE.c.etag == if_match_etag, _TABLE.c.id == meta_id ) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Meta', meta_id) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def put_current_user(user): if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.current_user.put(flask.request.json) current_password = values['current_password'] encrypted_password = user['password'] if not auth.check_passwords_equal(current_password, encrypted_password): raise dci_exc.DCIException('current_password invalid') new_password = values.get('new_password') if new_password: encrypted_password = auth.hash_password(new_password) etag = utils.gen_etag() query = _TABLE.update().where( sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == user['id'])).values({ 'etag': etag, 'fullname': values.get('fullname') or user['fullname'], 'email': values.get('email') or user['email'], 'timezone': values.get('timezone') or user['timezone'], 'password': encrypted_password, }) flask.g.db_conn.execute(query) return flask.Response(None, 204, headers={'ETag': etag}, content_type='application/json')
def authenticate(self): headers = self.request.headers auth_request = AuthRequest(method=self.request.method, endpoint=self.request.path, payload=self.request.get_json(silent=True), headers=headers, params=self.request.args.to_dict(flat=True)) hmac_signature = Signature(request=auth_request) self.identity = self.build_identity(auth_request.get_client_info()) secret = getattr(self.identity, 'api_secret', '') if not hmac_signature.is_valid(secret): raise dci_exc.DCIException( 'Authentication failed: signature invalid', status_code=401) if hmac_signature.is_expired(): raise dci_exc.DCIException( 'Authentication failed: signature expired', status_code=401) return True
def create_jobs(user): values = flask.request.json check_json_is_valid(create_job_schema, values) values.update(v1_utils.common_values_dict()) components_ids = values.pop('components') if user.is_not_remoteci(): raise dci_exc.DCIException('Only remoteci can create job') topic_id = values.get('topic_id') topic = v1_utils.verify_existence_and_get(topic_id, models.TOPICS) export_control.verify_access_to_topic(user, topic) previous_job_id = values.get('previous_job_id') if previous_job_id: v1_utils.verify_existence_and_get(previous_job_id, _TABLE) values.update({ 'status': 'new', 'remoteci_id': user.id, 'topic_id': topic_id, 'user_agent': flask.request.environ.get('HTTP_USER_AGENT'), 'client_version': flask.request.environ.get('HTTP_CLIENT_VERSION'), 'previous_job_id': previous_job_id, 'team_id': user.teams_ids[0], 'product_id': topic['product_id'], 'duration': 0 }) # create the job and feed the jobs_components table with flask.g.db_conn.begin(): query = _TABLE.insert().values(**values) flask.g.db_conn.execute(query) jobs_components_to_insert = [] for cmpt_id in components_ids: v1_utils.verify_existence_and_get(cmpt_id, models.COMPONENTS) jobs_components_to_insert.append({ 'job_id': values['id'], 'component_id': cmpt_id }) if jobs_components_to_insert: flask.g.db_conn.execute(models.JOIN_JOBS_COMPONENTS.insert(), jobs_components_to_insert) return flask.Response(json.dumps({'job': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def create_new_upgrade_job_from_an_existing_job(user): """Create a new job in the 'next topic' of the topic of the provided job_id.""" values = flask.request.json check_json_is_valid(upgrade_job_schema, values) values.update({ 'id': utils.gen_uuid(), 'created_at': get_utc_now().isoformat(), 'updated_at': get_utc_now().isoformat(), 'etag': utils.gen_etag(), 'status': 'new' }) original_job_id = values.pop('job_id') original_job = v1_utils.verify_existence_and_get(original_job_id, models.JOBS) if user.is_not_in_team(original_job['team_id']) and user.is_not_epm(): raise dci_exc.Unauthorized() # get the remoteci remoteci_id = str(original_job['remoteci_id']) remoteci = v1_utils.verify_existence_and_get(remoteci_id, models.REMOTECIS) values.update({'remoteci_id': remoteci_id}) # get the associated topic topic_id = str(original_job['topic_id']) topic = v1_utils.verify_existence_and_get(topic_id, models.TOPICS) values.update({ 'user_agent': flask.request.environ.get('HTTP_USER_AGENT'), 'client_version': flask.request.environ.get('HTTP_CLIENT_VERSION'), }) next_topic_id = topic['next_topic_id'] if not next_topic_id: raise dci_exc.DCIException("topic %s does not contains a next topic" % topic_id) topic = v1_utils.verify_existence_and_get(next_topic_id, models.TOPICS) product_id = topic['product_id'] # instantiate a new job in the next_topic_id # todo(yassine): make possible the upgrade to choose specific components values = _build_job(product_id, next_topic_id, remoteci, [], values, previous_job_id=original_job_id) return flask.Response(json.dumps({'job': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def get_topic_by_id(user, topic_id): args = schemas.args(flask.request.args.to_dict()) topic = v1_utils.verify_existence_and_get(topic_id, _TABLE) v1_utils.verify_team_in_topic(user, topic_id) if not auth.is_admin(user): if 'teams' in args['embed']: raise dci_exc.DCIException('embed=teams not authorized.', status_code=401) return base.get_resource_by_id(user, topic, _TABLE, _EMBED_MANY)
def _verify_existence_and_get_user(user_id): where_clause = _TABLE.c.id == user_id query = sql.select(_SELECT_WITHOUT_PASSWORD).where(where_clause) result = flask.g.db_conn.execute(query).fetchone() if result is None: raise dci_exc.DCIException('Resource "%s" not found.' % user_id, status_code=404) return result
def verify_user_in_team(user, team_id): """Verify that the user belongs to a given team. If the user is an admin then it belongs to all teams.""" if auth.is_admin(user): return if not auth.is_in_team(user, team_id): raise dci_exc.DCIException('User\'s team does not belongs to ' 'team %s.' % team_id, status_code=412)
def verify_team_in_topic(user, topic_id): """Verify that the user's team does belongs to the given topic. If the user is an admin then it belongs to all topics. """ if auth.is_admin(user): return if str(topic_id) not in user_topic_ids(user): raise dci_exc.DCIException('User team does not belongs to topic %s.' % topic_id, status_code=412)
def _get_column(table, columns, name): payload = { 'error': 'where key must have the following form ' '"key:value"' } if '.' in name: subtable_name, name = name.split('.') table_obj = embeds.EMBED_STRING_TO_OBJECT[table.name] try: table = table_obj[subtable_name] except KeyError: payload = {'valid_keys': list(table_obj.keys())} raise dci_exc.DCIException(err_msg % name, payload=payload) columns = get_columns_name_with_objects(table) if name not in columns: payload = {'valid_keys': list(columns.keys())} raise dci_exc.DCIException(err_msg % name, payload=payload) return getattr(table.c, name)
def authenticate(self): """Tries to authenticate a request using a signature as authentication mechanism. Sets self.identity to the authenticated entity for later use. """ # Get headers and extract information client_info = self.get_client_info() their_signature = self.request.headers.get('DCI-Auth-Signature') identity = self.get_identity(client_info['type'], client_info['id']) if identity is None: raise dci_exc.DCIException( 'Client %(type)s/%(id)s does not exist' % client_info, status_code=401) self.identity = identity if not self.verify_auth_signature(identity, client_info['timestamp'], their_signature): raise dci_exc.DCIException('Invalid signature.', status_code=401) return True
def get_last_components_by_type(component_types, topic_id, db_conn=None): """For each component type of a topic, get the last one.""" db_conn = db_conn or flask.g.db_conn _components = [] for ct in component_types: where_clause = sql.and_(models.COMPONENTS.c.type == ct, models.COMPONENTS.c.topic_id == topic_id, models.COMPONENTS.c.state == 'active') # noqa query = (sql.select([models.COMPONENTS]).where(where_clause).order_by( sql.desc(models.COMPONENTS.c.created_at))) component = db_conn.execute(query).fetchone() if component is None: msg = 'Component of type "%s" not found or not exported.' % ct raise dci_exc.DCIException(msg, status_code=412) if component['id'] in _components: msg = ('Component types %s malformed: type %s duplicated.' % (component_types, ct)) raise dci_exc.DCIException(msg, status_code=412) _components.append(component) return _components
def _get_embed_list(self, embed_joins): valid_embed = embed_joins.keys() embed_list = [] for embed_elem in self._embeds: left = embed_elem.split('.')[0] if embed_elem not in valid_embed: raise dci_exc.DCIException( 'Invalid embed element %s' % embed_elem, payload={'Valid elements': valid_embed}) if left not in embed_list: embed_list.append(left) embed_list.append(embed_elem) return sorted(set(embed_list))
def get_user_and_check_auth(self, username, password): """Check the combination username/password that is valid on the database. """ constraint = sql.or_(models.USERS.c.name == username, models.USERS.c.email == username) user = self.identity_from_db(models.USERS, constraint) if user is None: raise dci_exc.DCIException('User %s does not exists.' % username, status_code=401) return user, auth.check_passwords_equal(password, user.password)
def authenticate(self): auth_header = self.request.headers.get('Authorization').split(' ') if len(auth_header) != 2: return False bearer, token = auth_header conf = dci_config.generate_conf() try: decoded_token = auth.decode_jwt(token, conf['SSO_PUBLIC_KEY'], conf['SSO_CLIENT_ID']) except jwt_exc.DecodeError: raise dci_exc.DCIException('Invalid JWT token.', status_code=401) except jwt_exc.ExpiredSignatureError: raise dci_exc.DCIException('JWT token expired, please refresh.', status_code=401) sso_username = decoded_token['username'] self.identity = self._get_user_from_sso_username(sso_username) if self.identity is None: self.identity = self._create_user_and_get(decoded_token) if self.identity is None: return False