def create_users(user): values = v1_utils.common_values_dict(user) values.update(schemas.user.post(flask.request.json)) if not (auth.is_admin(user) or auth.is_admin_user(user, values['team_id'])): raise auth.UNAUTHORIZED role_id = values.get('role_id', auth.get_role_id('USER')) if not auth.is_admin(user) and role_id == auth.get_role_id('SUPER_ADMIN'): raise auth.UNAUTHORIZED values.update({ 'password': auth.hash_password(values.get('password')), 'role_id': role_id }) query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') # remove the password in the result for security reasons del values['password'] return flask.Response(json.dumps({'user': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def put_user(user, user_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.user.put(flask.request.json) puser = dict(_verify_existence_and_get_user(user_id)) if puser['id'] != str(user_id): if not (auth.is_admin(user) or auth.is_admin_user(user, puser['team_id'])): raise auth.UNAUTHORIZED # TODO(yassine): if the user wants to change the team, then check its done # by a super admin. ie. team_name='admin' values['etag'] = utils.gen_etag() if 'password' in values: values['password'] = auth.hash_password(values.get('password')) where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == user_id) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('User', user_id) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def get_all_tests(user, topic_id): args = schemas.args(flask.request.args.to_dict()) if not (auth.is_admin(user)): v1_utils.verify_team_in_topic(user, topic_id) v1_utils.verify_existence_and_get(topic_id, _TABLE) query = sql.select([models.TESTS]).\ select_from(models.JOIN_TOPICS_TESTS.join(models.TESTS)).\ where(models.JOIN_TOPICS_TESTS.c.topic_id == topic_id) T_COLUMNS = v1_utils.get_columns_name_with_objects(models.TESTS) sort_list = v1_utils.sort_query(args['sort'], T_COLUMNS) where_list = v1_utils.where_query(args['where'], models.TESTS, T_COLUMNS) query = v1_utils.add_sort_to_query(query, sort_list) query = v1_utils.add_where_to_query(query, where_list) if args.get('limit', None): query = query.limit(args.get('limit')) if args.get('offset', None): query = query.offset(args.get('offset')) rows = flask.g.db_conn.execute(query).fetchall() query_nb_rows = sql.select([func.count(models.TESTS.c.id)]). \ select_from(models.JOIN_TOPICS_TESTS.join(models.TESTS)). \ where(models.JOIN_TOPICS_TESTS.c.topic_id == topic_id) nb_rows = flask.g.db_conn.execute(query_nb_rows).scalar() res = flask.jsonify({'tests': rows, '_meta': {'count': nb_rows}}) res.status_code = 200 return res
def get_resource_by_id(user, resource, table, embed_many, ignore_columns=None): args = schemas.args(flask.request.args.to_dict()) resource_name = table.name[0:-1] resource_id = resource['id'] columns = v1_utils.get_columns_name_with_objects(table) query = v1_utils.QueryBuilder(table, args, columns, ignore_columns) if not auth.is_admin(user) and 'team_id' in resource: query.add_extra_condition(table.c.team_id == user['team_id']) if 'state' in resource: query.add_extra_condition(table.c.state != 'archived') query.add_extra_condition(table.c.id == resource_id) rows = query.execute(fetchall=True) rows = v1_utils.format_result(rows, table.name, args['embed'], embed_many) if len(rows) < 1: raise dci_exc.DCINotFound(resource_name, resource_id) resource = rows[0] res = flask.jsonify({resource_name: resource}) if 'etag' in resource: res.headers.add_header('ETag', resource['etag']) return res
def put_topic(user, topic_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.topic.put(flask.request.json) if not auth.is_admin(user): raise auth.UNAUTHORIZED def _verify_team_in_topic(user, topic_id): topic_id = v1_utils.verify_existence_and_get(topic_id, _TABLE, get_id=True) # verify user's team in the topic v1_utils.verify_team_in_topic(user, topic_id) return topic_id topic_id = _verify_team_in_topic(user, topic_id) if 'next_topic' in values and values['next_topic']: _verify_team_in_topic(user, values['next_topic']) values['etag'] = utils.gen_etag() where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == topic_id) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Topic', topic_id) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def create_remotecis(user): values = v1_utils.common_values_dict(user) values.update(schemas.remoteci.post(flask.request.json)) # If it's not a super admin nor belongs to the same team_id if not (auth.is_admin(user) or auth.is_in_team(user, values.get('team_id'))): raise auth.UNAUTHORIZED values.update({ 'data': values.get('data', {}), # XXX(fc): this should be populated as a default value from the # model, but we don't return values from the database :( 'api_secret': signature.gen_secret(), }) query = _TABLE.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'name') return flask.Response(json.dumps({'remoteci': values}), 201, headers={'ETag': values['etag']}, content_type='application/json')
def put_api_secret(user, r_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) remoteci = v1_utils.verify_existence_and_get(r_id, _TABLE) if not (auth.is_admin(user) or auth.is_in_team(user, remoteci['team_id'])): raise auth.UNAUTHORIZED where_clause = sql.and_( _TABLE.c.etag == if_match_etag, _TABLE.c.id == r_id, ) values = {'api_secret': signature.gen_secret(), 'etag': utils.gen_etag()} query = (_TABLE.update().where(where_clause).values(**values)) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('RemoteCI', r_id) res = flask.jsonify(({ 'id': r_id, 'etag': values['etag'], 'api_secret': values['api_secret'] })) res.headers.add_header('ETag', values['etag']) return res
def delete_job_by_id(user, j_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) job = v1_utils.verify_existence_and_get(j_id, _TABLE) if not (auth.is_admin(user) or auth.is_in_team(user, job['team_id'])): raise auth.UNAUTHORIZED with flask.g.db_conn.begin(): values = {'state': 'archived'} where_clause = sql.and_(_TABLE.c.id == j_id, _TABLE.c.etag == if_match_etag) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIDeleteConflict('Job', j_id) for model in [models.FILES]: query = model.update().where(model.c.job_id == j_id).values( **values ) flask.g.db_conn.execute(query) return flask.Response(None, 204, content_type='application/json')
def get_all_results_from_jobs(user, j_id): """Get all results from job. """ job = v1_utils.verify_existence_and_get(j_id, _TABLE) # If it's an admin or belongs to the same team if not(auth.is_admin(user) or auth.is_in_team(user, job['team_id'])): raise auth.UNAUTHORIZED swift = dci_config.get_store('files') job_files = json.loads(files.get_all_files(j_id).response[0])['files'] r_files = [file for file in job_files if file['mime'] == 'application/junit'] results = [] for file in r_files: file_path = swift.build_file_path(file['team_id'], j_id, file['id']) content_file = swift.get_object(file_path) data = tsfm.junit2dict(content_file) results.append({'filename': file['name'], 'name': file['name'], 'total': data['total'], 'failures': data['failures'], 'errors': data['errors'], 'skips': data['skips'], 'time': data['time'], 'success': data['success'], 'testscases': data['testscases']}) return flask.jsonify({'results': results, '_meta': {'count': len(results)}})
def put_remoteci(user, r_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.remoteci.put(flask.request.json) remoteci = v1_utils.verify_existence_and_get(r_id, _TABLE) if 'data' in values: remoteci_data = get_remoteci_data_json(user, r_id) remoteci_data.update(values['data']) values['data'] = {k: v for k, v in remoteci_data.items() if v} if not (auth.is_admin(user) or auth.is_in_team(user, remoteci['team_id'])): raise auth.UNAUTHORIZED values['etag'] = utils.gen_etag() where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.state != 'archived', _TABLE.c.id == r_id) query = (_TABLE.update().where(where_clause).values(**values)) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('RemoteCI', r_id) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def delete_team_by_id(user, t_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) if not auth.is_admin(user): raise auth.UNAUTHORIZED v1_utils.verify_existence_and_get(t_id, _TABLE) with flask.g.db_conn.begin(): values = {'state': 'archived'} where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == t_id) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIDeleteConflict('Team', t_id) for model in [ models.FILES, models.TESTS, models.REMOTECIS, models.USERS, models.JOBS ]: query = model.update().where(model.c.team_id == t_id).values( **values) flask.g.db_conn.execute(query) return flask.Response(None, 204, content_type='application/json')
def get_all_jobs(user, jd_id=None): """Get all jobs. If jd_id is not None, then return all the jobs with a jobdefinition pointed by jd_id. """ # get the diverse parameters args = schemas.args(flask.request.args.to_dict()) # build the query thanks to the QueryBuilder class query = v1_utils.QueryBuilder(_TABLE, args, _JOBS_COLUMNS, ['configuration']) # add extra conditions for filtering # # If not admin then restrict the view to the team if not auth.is_admin(user): query.add_extra_condition(_TABLE.c.team_id == user['team_id']) # # If jd_id not None, then filter by jobdefinition_id if jd_id is not None: query.add_extra_condition(_TABLE.c.jobdefinition_id == jd_id) # # Get only the non archived jobs query.add_extra_condition(_TABLE.c.state != 'archived') nb_rows = query.get_number_of_rows() rows = query.execute(fetchall=True) rows = v1_utils.format_result(rows, _TABLE.name, args['embed'], _EMBED_MANY) return flask.jsonify({'jobs': rows, '_meta': {'count': nb_rows}})
def put_team(user, t_id): # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) values = schemas.team.put(flask.request.json) if not (auth.is_admin(user) or auth.is_admin_user(user, t_id)): raise auth.UNAUTHORIZED v1_utils.verify_existence_and_get(t_id, _TABLE) values['etag'] = utils.gen_etag() where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == t_id) query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Team', t_id) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def upload_component_file(user, c_id): if not auth.is_admin(user): raise auth.UNAUTHORIZED COMPONENT_FILES = models.COMPONENT_FILES component = v1_utils.verify_existence_and_get(c_id, _TABLE) swift = dci_config.get_store('components') file_id = utils.gen_uuid() file_path = swift.build_file_path(component['topic_id'], c_id, file_id) swift = dci_config.get_store('components') swift.upload(file_path, flask.request.stream) s_file = swift.head(file_path) values = dict.fromkeys(['md5', 'mime', 'component_id', 'name']) values.update({ 'id': file_id, 'component_id': c_id, 'name': file_id, 'created_at': datetime.datetime.utcnow().isoformat(), 'md5': s_file['etag'], 'mime': s_file['content-type'], 'size': s_file['content-length'] }) query = COMPONENT_FILES.insert().values(**values) flask.g.db_conn.execute(query) result = json.dumps({'component_file': values}) return flask.Response(result, 201, 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_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 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 get_role_by_id(user, role_id): role = v1_utils.verify_existence_and_get(role_id, _TABLE) if user['role_id'] != role_id and \ user['role_id'] not in [auth.get_role_id('ADMIN'), auth.get_role_id('SUPER_ADMIN')]: raise auth.UNAUTHORIZED if not auth.is_admin(user) and auth.get_role_id('SUPER_ADMIN') == role_id: raise auth.UNAUTHORIZED return base.get_resource_by_id(user, role, _TABLE, _EMBED_MANY)
def get_remoteci_data_json(user, r_id): query = v1_utils.QueryBuilder(_TABLE, {}, _R_COLUMNS) if not auth.is_admin(user): query.add_extra_condition(_TABLE.c.team_id == user['team_id']) query.add_extra_condition(_TABLE.c.id == r_id) row = query.execute(fetchone=True) if row is None: raise dci_exc.DCINotFound('RemoteCI', r_id) return row['remotecis_data']
def purge_archived_resources(user, table): """Remove the entries to be purged from the database. """ if not auth.is_admin(user): raise auth.UNAUTHORIZED where_clause = sql.and_( table.c.state == 'archived' ) query = table.delete().where(where_clause) flask.g.db_conn.execute(query) return flask.Response(None, 204, content_type='application/json')
def get_all_roles(user): args = schemas.args(flask.request.args.to_dict()) query = v1_utils.QueryBuilder(_TABLE, args, _T_COLUMNS) query.add_extra_condition(_TABLE.c.state != 'archived') if not auth.is_admin(user): query.add_extra_condition(_TABLE.c.label != 'SUPER_ADMIN') if not (auth.is_admin(user) or user['role_id'] == auth.get_role_id('PRODUCT_OWNER')): query.add_extra_condition(_TABLE.c.label != 'PRODUCT_OWNER') if user['role_id'] == auth.get_role_id('USER'): query.add_extra_condition(_TABLE.c.id == user['role_id']) nb_rows = query.get_number_of_rows() rows = query.execute(fetchall=True) rows = v1_utils.format_result(rows, _TABLE.name, args['embed'], _EMBED_MANY) return flask.jsonify({'roles': rows, '_meta': {'count': nb_rows}})
def get_to_purge_archived_resources(user, table): """List the entries to be purged from the database. """ if not auth.is_admin(user): raise auth.UNAUTHORIZED where_clause = sql.and_( table.c.state == 'archived' ) query = sql.select([table]).where(where_clause) result = flask.g.db_conn.execute(query).fetchall() return flask.jsonify({table.name: result, '_meta': {'count': len(result)}})
def delete_test_from_topic(user, t_id, test_id): if not auth.is_admin(user): v1_utils.verify_team_in_topic(user, t_id) v1_utils.verify_existence_and_get(test_id, _TABLE) JDC = models.JOIN_REMOTECIS_TESTS where_clause = sql.and_(JDC.c.topic_id == t_id, JDC.c.test_id == test_id) query = JDC.delete().where(where_clause) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Test', test_id) return flask.Response(None, 204, content_type='application/json')
def get_all_teams_from_topic(user, topic_id): if not auth.is_admin(user): raise auth.UNAUTHORIZED topic_id = v1_utils.verify_existence_and_get(topic_id, _TABLE, get_id=True) # Get all teams which belongs to a given topic JTT = models.JOINS_TOPICS_TEAMS query = (sql.select([models.TEAMS]).select_from(JTT.join( models.TEAMS)).where(JTT.c.topic_id == topic_id)) rows = flask.g.db_conn.execute(query) res = flask.jsonify({'teams': rows, '_meta': {'count': rows.rowcount}}) return res
def search(user): values = schemas.search.post(flask.request.json) if values['refresh']: flask.g.es_conn.refresh() if auth.is_admin(user): res = flask.g.es_conn.search_content(values['pattern']) else: res = flask.g.es_conn.search_content(values['pattern'], user['team_id']) result = json.jsonify({'logs': res['hits']}) return result
def add_test_to_topic(user, topic_id): if not auth.is_admin(user): raise auth.UNAUTHORIZED data_json = flask.request.json values = {'topic_id': topic_id, 'test_id': data_json.get('test_id', None)} v1_utils.verify_existence_and_get(topic_id, _TABLE) query = models.JOIN_TOPICS_TESTS.insert().values(**values) try: flask.g.db_conn.execute(query) except sa_exc.IntegrityError: raise dci_exc.DCICreationConflict(_TABLE.name, 'topic_id, test_id') result = json.dumps(values) return flask.Response(result, 201, content_type='application/json')
def delete_jobstate_by_id(user, js_id): jobstate = v1_utils.verify_existence_and_get(js_id, _TABLE) if not(auth.is_admin(user) or auth.is_in_team(user, jobstate['team_id'])): raise auth.UNAUTHORIZED where_clause = _TABLE.c.id == js_id query = _TABLE.delete().where(where_clause) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIDeleteConflict('Jobstate', js_id) return flask.Response(None, 204, content_type='application/json')
def delete_permission_from_role(user, role_id, permission_id): if not auth.is_admin(user): raise auth.UNAUTHORIZED v1_utils.verify_existence_and_get(role_id, _TABLE) JRP = models.JOIN_ROLES_PERMISSIONS where_clause = sql.and_(JRP.c.role_id == role_id, JRP.c.permission_id == permission_id) query = JRP.delete().where(where_clause) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Role', role_id) return flask.Response(None, 204, content_type='application/json')
def update_job_by_id(user, job_id): """Update a job """ # get If-Match header if_match_etag = utils.check_and_get_etag(flask.request.headers) # get the diverse parameters values = schemas.job.put(flask.request.json) job = v1_utils.verify_existence_and_get(job_id, _TABLE) # If it's an admin or belongs to the same team if not(auth.is_admin(user) or auth.is_in_team(user, job['team_id'])): raise auth.UNAUTHORIZED # Update jobstate if needed status = values.get('status') if status and job.status != status: jobstates.insert_jobstate(user, { 'status': status, 'job_id': job_id }) where_clause = sql.and_(_TABLE.c.etag == if_match_etag, _TABLE.c.id == job_id) values['etag'] = utils.gen_etag() query = _TABLE.update().where(where_clause).values(**values) result = flask.g.db_conn.execute(query) if not result.rowcount: raise dci_exc.DCIConflict('Job', job_id) if values.get('status') == "failure": _TEAMS = models.TEAMS where_clause = sql.expression.and_( _TEAMS.c.id == job['team_id'] ) query = (sql.select([_TEAMS]).where(where_clause)) team_info = flask.g.db_conn.execute(query).fetchone() if team_info['notification'] is True: if team_info['email'] is not None: msg = {'event': 'notification', 'email': team_info['email'], 'job_id': str(job['id'])} flask.g.sender.send_json(msg) return flask.Response(None, 204, headers={'ETag': values['etag']}, content_type='application/json')
def get_all_teams(user): args = schemas.args(flask.request.args.to_dict()) query = v1_utils.QueryBuilder(_TABLE, args, _T_COLUMNS) if not auth.is_admin(user): query.add_extra_condition(_TABLE.c.id == user['team_id']) query.add_extra_condition(_TABLE.c.state != 'archived') nb_rows = query.get_number_of_rows() rows = query.execute(fetchall=True) rows = v1_utils.format_result(rows, _TABLE.name, args['embed'], _EMBED_MANY) return flask.jsonify({'teams': rows, '_meta': {'count': nb_rows}})