refactor(endpoints/api/superuser*): refactored code behind db model
this moves all the db model code behind an interface in prep for v2-2 Issue: https://coreosdev.atlassian.net/browse/QUAY-750 - [ ] It works! - [ ] Comments provide sufficient explanations for the next contributor - [ ] Tests cover changes and corner cases - [ ] Follows Quay syntax patterns and format
This commit is contained in:
		
							parent
							
								
									3688b6a8df
								
							
						
					
					
						commit
						6c29ec873a
					
				
					 4 changed files with 751 additions and 177 deletions
				
			
		|  | @ -29,7 +29,9 @@ from endpoints.api import (ApiResource, nickname, resource, validate_json_reques | |||
| from endpoints.api.build import build_status_view, get_logs_or_log_url | ||||
| from data import model, database | ||||
| from data.database import ServiceKeyApprovalType | ||||
| from endpoints.exception import NotFound | ||||
| from endpoints.api.superuser_models_pre_oci import pre_oci_model, ServiceKeyDoesNotExist, ServiceKeyAlreadyApproved, \ | ||||
|   InvalidRepositoryBuildException | ||||
| from endpoints.exception import NotFound, InvalidResponse | ||||
| from util.useremails import send_confirmation_email, send_recovery_email | ||||
| from util.license import decode_license, LicenseDecodeError | ||||
| from util.security.ssl import load_certificate, CertInvalidException | ||||
|  | @ -39,11 +41,7 @@ from _init import ROOT_DIR | |||
| logger = logging.getLogger(__name__) | ||||
| 
 | ||||
| 
 | ||||
| def _validate_logs_arguments(start_time, end_time, performer_name): | ||||
|   performer = None | ||||
|   if performer_name: | ||||
|     performer = model.user.get_user(performer_name) | ||||
| 
 | ||||
| def _validate_logs_arguments(start_time, end_time): | ||||
|   if start_time: | ||||
|     try: | ||||
|       start_time = datetime.strptime(start_time + ' UTC', '%m/%d/%Y %Z') | ||||
|  | @ -63,7 +61,7 @@ def _validate_logs_arguments(start_time, end_time, performer_name): | |||
|   if not end_time: | ||||
|     end_time = datetime.today() | ||||
| 
 | ||||
|   return start_time, end_time, performer | ||||
|   return start_time, end_time | ||||
| 
 | ||||
| 
 | ||||
| def get_immediate_subdirectories(directory): | ||||
|  | @ -89,7 +87,7 @@ class SuperUserGetLogsForService(ApiResource): | |||
|   def get(self, service): | ||||
|     """ Returns the logs for the specific service. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       if not service in get_services(): | ||||
|       if service not in get_services(): | ||||
|         abort(404) | ||||
| 
 | ||||
|       logs = [] | ||||
|  | @ -130,37 +128,6 @@ class SuperUserSystemLogServices(ApiResource): | |||
|     abort(403) | ||||
| 
 | ||||
| 
 | ||||
| def aggregated_log_view(log, kinds, start_time): | ||||
|   # Because we aggregate based on the day of the month in SQL, we only have that information. | ||||
|   # Therefore, create a synthetic date based on the day and the month of the start time. | ||||
|   # Logs are allowed for a maximum period of one week, so this calculation should always work. | ||||
|   synthetic_date = datetime(start_time.year, start_time.month, int(log.day), tzinfo=get_localzone()) | ||||
|   if synthetic_date.day < start_time.day: | ||||
|     synthetic_date = synthetic_date + relativedelta(months=1) | ||||
| 
 | ||||
|   view = { | ||||
|     'kind': kinds[log.kind_id], | ||||
|     'count': log.count, | ||||
|     'datetime': format_date(synthetic_date), | ||||
|   } | ||||
| 
 | ||||
|   return view | ||||
| 
 | ||||
| 
 | ||||
| def get_aggregate_logs(start_time, end_time, performer_name=None, repository=None, namespace=None, | ||||
|                        ignore=None): | ||||
|   (start_time, end_time, performer) = _validate_logs_arguments(start_time, end_time, performer_name) | ||||
| 
 | ||||
|   kinds = model.log.get_log_entry_kinds() | ||||
|   aggregated_logs = model.log.get_aggregated_logs(start_time, end_time, performer=performer, | ||||
|                                                   repository=repository, namespace=namespace, | ||||
|                                                   ignore=ignore) | ||||
| 
 | ||||
|   return { | ||||
|     'aggregated': [aggregated_log_view(log, kinds, start_time) for log in aggregated_logs] | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| @resource('/v1/superuser/aggregatelogs') | ||||
| @internal_only | ||||
| class SuperUserAggregateLogs(ApiResource): | ||||
|  | @ -175,10 +142,12 @@ class SuperUserAggregateLogs(ApiResource): | |||
|   def get(self, parsed_args): | ||||
|     """ Returns the aggregated logs for the current system. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       start_time = parsed_args['starttime'] | ||||
|       end_time = parsed_args['endtime'] | ||||
|       (start_time, end_time) = _validate_logs_arguments(parsed_args['starttime'], parsed_args['endtime']) | ||||
|       aggregated_logs = pre_oci_model.get_aggregated_logs(start_time, end_time) | ||||
| 
 | ||||
|       return get_aggregate_logs(start_time, end_time) | ||||
|       return { | ||||
|         'aggregated': [log.to_dict() for log in aggregated_logs] | ||||
|       } | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -186,59 +155,6 @@ class SuperUserAggregateLogs(ApiResource): | |||
| LOGS_PER_PAGE = 20 | ||||
| 
 | ||||
| 
 | ||||
| def log_view(log, kinds, include_namespace): | ||||
|   view = { | ||||
|     'kind': kinds[log.kind_id], | ||||
|     'metadata': json.loads(log.metadata_json), | ||||
|     'ip': log.ip, | ||||
|     'datetime': format_date(log.datetime), | ||||
|   } | ||||
| 
 | ||||
|   if log.performer and log.performer.username: | ||||
|     view['performer'] = { | ||||
|       'kind': 'user', | ||||
|       'name': log.performer.username, | ||||
|       'is_robot': log.performer.robot, | ||||
|       'avatar': avatar.get_data_for_user(log.performer), | ||||
|     } | ||||
| 
 | ||||
|   if include_namespace: | ||||
|     if log.account and log.account.username: | ||||
|       if log.account.organization: | ||||
|         view['namespace'] = { | ||||
|           'kind': 'org', | ||||
|           'name': log.account.username, | ||||
|           'avatar': avatar.get_data_for_org(log.account), | ||||
|         } | ||||
|       else: | ||||
|         view['namespace'] = { | ||||
|           'kind': 'user', | ||||
|           'name': log.account.username, | ||||
|           'avatar': avatar.get_data_for_user(log.account), | ||||
|         } | ||||
| 
 | ||||
|   return view | ||||
| 
 | ||||
| 
 | ||||
| def get_logs(start_time, end_time, performer_name=None, repository=None, namespace=None, | ||||
|              page_token=None, ignore=None): | ||||
|   (start_time, end_time, performer) = _validate_logs_arguments(start_time, end_time, performer_name) | ||||
|   kinds = model.log.get_log_entry_kinds() | ||||
|   logs_query = model.log.get_logs_query(start_time, end_time, performer=performer, | ||||
|                                         repository=repository, namespace=namespace, | ||||
|                                         ignore=ignore) | ||||
| 
 | ||||
|   logs, next_page_token = model.modelutil.paginate(logs_query, database.LogEntry, descending=True, | ||||
|                                                    page_token=page_token, limit=LOGS_PER_PAGE) | ||||
| 
 | ||||
|   include_namespace = namespace is None and repository is None | ||||
|   return { | ||||
|            'start_time': format_date(start_time), | ||||
|            'end_time': format_date(end_time), | ||||
|            'logs': [log_view(log, kinds, include_namespace) for log in logs], | ||||
|          }, next_page_token | ||||
| 
 | ||||
| 
 | ||||
| @resource('/v1/superuser/logs') | ||||
| @internal_only | ||||
| @show_if(features.SUPER_USERS) | ||||
|  | @ -259,8 +175,14 @@ class SuperUserLogs(ApiResource): | |||
|     if SuperUserPermission().can(): | ||||
|       start_time = parsed_args['starttime'] | ||||
|       end_time = parsed_args['endtime'] | ||||
|       (start_time, end_time) = _validate_logs_arguments(start_time, end_time) | ||||
|       log_page = pre_oci_model.get_logs_query(start_time, end_time, page_token=page_token) | ||||
| 
 | ||||
|       return get_logs(start_time, end_time, page_token=page_token) | ||||
|       return { | ||||
|                'start_time': format_date(start_time), | ||||
|                'end_time': format_date(end_time), | ||||
|                'logs': [log.to_dict() for log in log_page.logs], | ||||
|              }, log_page.next_page_token | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -325,9 +247,8 @@ class SuperUserOrganizationList(ApiResource): | |||
|   def get(self): | ||||
|     """ Returns a list of all organizations in the system. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       orgs = model.organization.get_organizations() | ||||
|       return { | ||||
|         'organizations': [org_view(org) for org in orgs] | ||||
|         'organizations': [org.to_dict() for org in pre_oci_model.get_organizations()] | ||||
|       } | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -364,9 +285,9 @@ class SuperUserList(ApiResource): | |||
|   def get(self): | ||||
|     """ Returns a list of all users in the system. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       users = model.user.get_active_users() | ||||
|       users = pre_oci_model.get_active_users() | ||||
|       return { | ||||
|         'users': [user_view(user) for user in users] | ||||
|         'users': [user.to_dict() for user in users] | ||||
|       } | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -391,14 +312,9 @@ class SuperUserList(ApiResource): | |||
|       # Create the user. | ||||
|       username = user_information['username'] | ||||
|       email = user_information.get('email') | ||||
|       prompts = model.user.get_default_user_prompts(features) | ||||
|       user = model.user.create_user(username, password, email, auto_verify=not features.MAILING, | ||||
|                                     email_required=features.MAILING, prompts=prompts) | ||||
| 
 | ||||
|       # If mailing is turned on, send the user a verification email. | ||||
|       install_user, confirmation_code = pre_oci_model.create_install_user(username, password, email) | ||||
|       if features.MAILING: | ||||
|         confirmation = model.user.create_confirm_email_code(user) | ||||
|         send_confirmation_email(user.username, user.email, confirmation.code) | ||||
|         send_confirmation_email(install_user.username, install_user.email, confirmation_code) | ||||
| 
 | ||||
|       return { | ||||
|         'username': username, | ||||
|  | @ -427,15 +343,15 @@ class SuperUserSendRecoveryEmail(ApiResource): | |||
|       abort(400) | ||||
| 
 | ||||
|     if SuperUserPermission().can(): | ||||
|       user = model.user.get_nonrobot_user(username) | ||||
|       if not user: | ||||
|       user = pre_oci_model.get_nonrobot_user(username) | ||||
|       if user is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       if superusers.is_superuser(username): | ||||
|         abort(403) | ||||
| 
 | ||||
|       code = model.user.create_reset_password_email_code(user.email) | ||||
|       send_recovery_email(user.email, code.code) | ||||
|       code = pre_oci_model.create_reset_password_email_code(user.email) | ||||
|       send_recovery_email(user.email, code) | ||||
|       return { | ||||
|         'email': user.email | ||||
|       } | ||||
|  | @ -478,11 +394,11 @@ class SuperUserManagement(ApiResource): | |||
|   def get(self, username): | ||||
|     """ Returns information about the specified user. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       user = model.user.get_nonrobot_user(username) | ||||
|       if not user: | ||||
|       user = pre_oci_model.get_nonrobot_user(username) | ||||
|       if user is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       return user_view(user) | ||||
|       return user.to_dict() | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -493,14 +409,14 @@ class SuperUserManagement(ApiResource): | |||
|   def delete(self, username): | ||||
|     """ Deletes the specified user. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       user = model.user.get_nonrobot_user(username) | ||||
|       if not user: | ||||
|       user = pre_oci_model.get_nonrobot_user(username) | ||||
|       if user is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       if superusers.is_superuser(username): | ||||
|         abort(403) | ||||
| 
 | ||||
|       model.user.delete_user(user, all_queues, force=True) | ||||
|       pre_oci_model.delete_user(username) | ||||
|       return '', 204 | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -513,8 +429,8 @@ class SuperUserManagement(ApiResource): | |||
|   def put(self, username): | ||||
|     """ Updates information about the specified user. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       user = model.user.get_nonrobot_user(username) | ||||
|       if not user: | ||||
|       user = pre_oci_model.get_nonrobot_user(username) | ||||
|       if user is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       if superusers.is_superuser(username): | ||||
|  | @ -526,19 +442,18 @@ class SuperUserManagement(ApiResource): | |||
|         if app.config['AUTHENTICATION_TYPE'] != 'Database': | ||||
|           abort(400) | ||||
| 
 | ||||
|         model.user.change_password(user, user_data['password']) | ||||
|         pre_oci_model.change_password(username, user_data['password']) | ||||
| 
 | ||||
|       if 'email' in user_data: | ||||
|         # Ensure that we are using database auth. | ||||
|         if app.config['AUTHENTICATION_TYPE'] != 'Database': | ||||
|           abort(400) | ||||
| 
 | ||||
|         model.user.update_email(user, user_data['email'], auto_verify=True) | ||||
|         pre_oci_model.update_email(username, user_data['email'], auto_verify=True) | ||||
| 
 | ||||
|       if 'enabled' in user_data: | ||||
|         # Disable/enable the user. | ||||
|         user.enabled = bool(user_data['enabled']) | ||||
|         user.save() | ||||
|         pre_oci_model.update_enabled(username, bool(user_data['enabled'])) | ||||
| 
 | ||||
|       if 'superuser' in user_data: | ||||
|         config_object = config_provider.get_config() | ||||
|  | @ -552,7 +467,11 @@ class SuperUserManagement(ApiResource): | |||
|         config_object['SUPER_USERS'] = list(superusers_set) | ||||
|         config_provider.save_config(config_object) | ||||
| 
 | ||||
|       return user_view(user, password=user_data.get('password')) | ||||
|       return_value = user.to_dict() | ||||
|       if user_data.get('password') is not None: | ||||
|         return_value['encrypted_password'] = authentication.encrypt_user_password(user_data.get('password')) | ||||
| 
 | ||||
|       return return_value | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -575,23 +494,14 @@ class SuperUserTakeOwnership(ApiResource): | |||
|       if superusers.is_superuser(namespace): | ||||
|         abort(400) | ||||
| 
 | ||||
|       entity = model.user.get_user_or_org(namespace) | ||||
|       if entity is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       authed_user = get_authenticated_user() | ||||
|       was_user = not entity.organization | ||||
|       if entity.organization: | ||||
|         # Add the superuser as an admin to the owners team of the org. | ||||
|         model.organization.add_user_as_admin(authed_user, entity) | ||||
|       else: | ||||
|         # If the entity is a user, convert it to an organization and add the current superuser | ||||
|         # as the admin. | ||||
|         model.organization.convert_user_to_organization(entity, get_authenticated_user()) | ||||
|       entity_id, was_user = pre_oci_model.take_ownership(namespace, authed_user) | ||||
|       if entity_id is None: | ||||
|         abort(404) | ||||
| 
 | ||||
|       # Log the change. | ||||
|       log_metadata = { | ||||
|         'entity_id': entity.id, | ||||
|         'entity_id': entity_id, | ||||
|         'namespace': namespace, | ||||
|         'was_user': was_user, | ||||
|         'superuser': authed_user.username, | ||||
|  | @ -633,9 +543,7 @@ class SuperUserOrganizationManagement(ApiResource): | |||
|   def delete(self, name): | ||||
|     """ Deletes the specified organization. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       org = model.organization.get_organization(name) | ||||
| 
 | ||||
|       model.user.delete_user(org, all_queues) | ||||
|       pre_oci_model.delete_organization(name) | ||||
|       return '', 204 | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -648,13 +556,10 @@ class SuperUserOrganizationManagement(ApiResource): | |||
|   def put(self, name): | ||||
|     """ Updates information about the specified user. """ | ||||
|     if SuperUserPermission().can(): | ||||
|       org = model.organization.get_organization(name) | ||||
|       org_data = request.get_json() | ||||
| 
 | ||||
|       if 'name' in org_data: | ||||
|         org = model.user.change_username(org.id, org_data['name']) | ||||
| 
 | ||||
|       return org_view(org) | ||||
|       old_name = org_data['name'] if 'name' in org_data else None | ||||
|       org = pre_oci_model.change_organization_name(name, old_name) | ||||
|       return org.to_dict() | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -722,10 +627,10 @@ class SuperUserServiceKeyManagement(ApiResource): | |||
|   @require_scope(scopes.SUPERUSER) | ||||
|   def get(self): | ||||
|     if SuperUserPermission().can(): | ||||
|       keys = model.service_keys.list_all_keys() | ||||
|       keys = pre_oci_model.list_all_service_keys() | ||||
| 
 | ||||
|       return jsonify({ | ||||
|         'keys': [key_view(key) for key in keys], | ||||
|         'keys': [key.to_dict() for key in keys], | ||||
|       }) | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -760,16 +665,16 @@ class SuperUserServiceKeyManagement(ApiResource): | |||
|       }) | ||||
| 
 | ||||
|       # Generate a key with a private key that we *never save*. | ||||
|       (private_key, key) = model.service_keys.generate_service_key(body['service'], expiration_date, | ||||
|                                                                    metadata=metadata, | ||||
|                                                                    name=body.get('name', '')) | ||||
|       (private_key, key_id) = pre_oci_model.generate_service_key(body['service'], expiration_date, | ||||
|                                                                  metadata=metadata, | ||||
|                                                                  name=body.get('name', '')) | ||||
|       # Auto-approve the service key. | ||||
|       model.service_keys.approve_service_key(key.kid, user, ServiceKeyApprovalType.SUPERUSER, | ||||
|                                              notes=body.get('notes', '')) | ||||
|       pre_oci_model.approve_service_key(key_id, user, ServiceKeyApprovalType.SUPERUSER, | ||||
|                                         notes=body.get('notes', '')) | ||||
| 
 | ||||
|       # Log the creation and auto-approval of the service key. | ||||
|       key_log_metadata = { | ||||
|         'kid': key.kid, | ||||
|         'kid': key_id, | ||||
|         'preshared': True, | ||||
|         'service': body['service'], | ||||
|         'name': body.get('name', ''), | ||||
|  | @ -781,7 +686,7 @@ class SuperUserServiceKeyManagement(ApiResource): | |||
|       log_action('service_key_approve', None, key_log_metadata) | ||||
| 
 | ||||
|       return jsonify({ | ||||
|         'kid': key.kid, | ||||
|         'kid': key_id, | ||||
|         'name': body.get('name', ''), | ||||
|         'service': body['service'], | ||||
|         'public_key': private_key.publickey().exportKey('PEM'), | ||||
|  | @ -824,9 +729,9 @@ class SuperUserServiceKey(ApiResource): | |||
|   def get(self, kid): | ||||
|     if SuperUserPermission().can(): | ||||
|       try: | ||||
|         key = model.service_keys.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|         return jsonify(key_view(key)) | ||||
|       except model.service_keys.ServiceKeyDoesNotExist: | ||||
|         key = pre_oci_model.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|         return jsonify(key.to_dict()) | ||||
|       except ServiceKeyDoesNotExist: | ||||
|         abort(404) | ||||
| 
 | ||||
|     abort(403) | ||||
|  | @ -840,8 +745,8 @@ class SuperUserServiceKey(ApiResource): | |||
|     if SuperUserPermission().can(): | ||||
|       body = request.get_json() | ||||
|       try: | ||||
|         key = model.service_keys.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|       except model.service_keys.ServiceKeyDoesNotExist: | ||||
|         key = pre_oci_model.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|       except ServiceKeyDoesNotExist: | ||||
|         abort(404) | ||||
| 
 | ||||
|       key_log_metadata = { | ||||
|  | @ -868,14 +773,14 @@ class SuperUserServiceKey(ApiResource): | |||
|         }) | ||||
| 
 | ||||
|         log_action('service_key_extend', None, key_log_metadata) | ||||
|         model.service_keys.set_key_expiration(kid, expiration_date) | ||||
|         pre_oci_model.set_key_expiration(kid, expiration_date) | ||||
| 
 | ||||
|       if 'name' in body or 'metadata' in body: | ||||
|         model.service_keys.update_service_key(kid, body.get('name'), body.get('metadata')) | ||||
|         pre_oci_model.update_service_key(kid, body.get('name'), body.get('metadata')) | ||||
|         log_action('service_key_modify', None, key_log_metadata) | ||||
| 
 | ||||
|       updated_key = model.service_keys.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|       return jsonify(key_view(updated_key)) | ||||
|       updated_key = pre_oci_model.get_service_key(kid, approved_only=False, alive_only=False) | ||||
|       return jsonify(updated_key.to_dict()) | ||||
| 
 | ||||
|     abort(403) | ||||
| 
 | ||||
|  | @ -886,8 +791,8 @@ class SuperUserServiceKey(ApiResource): | |||
|   def delete(self, kid): | ||||
|     if SuperUserPermission().can(): | ||||
|       try: | ||||
|         key = model.service_keys.delete_service_key(kid) | ||||
|       except model.service_keys.ServiceKeyDoesNotExist: | ||||
|         key = pre_oci_model.delete_service_key(kid) | ||||
|       except ServiceKeyDoesNotExist: | ||||
|         abort(404) | ||||
| 
 | ||||
|       key_log_metadata = { | ||||
|  | @ -934,8 +839,8 @@ class SuperUserServiceKeyApproval(ApiResource): | |||
|       notes = request.get_json().get('notes', '') | ||||
|       approver = get_authenticated_user() | ||||
|       try: | ||||
|         key = model.service_keys.approve_service_key(kid, approver, ServiceKeyApprovalType.SUPERUSER, | ||||
|                                                      notes=notes) | ||||
|         key = pre_oci_model.approve_service_key(kid, approver, ServiceKeyApprovalType.SUPERUSER, | ||||
|                                                 notes=notes) | ||||
| 
 | ||||
|         # Log the approval of the service key. | ||||
|         key_log_metadata = { | ||||
|  | @ -946,9 +851,9 @@ class SuperUserServiceKeyApproval(ApiResource): | |||
|         } | ||||
| 
 | ||||
|         log_action('service_key_approve', None, key_log_metadata) | ||||
|       except model.ServiceKeyDoesNotExist: | ||||
|       except ServiceKeyDoesNotExist: | ||||
|         abort(404) | ||||
|       except model.ServiceKeyAlreadyApproved: | ||||
|       except ServiceKeyAlreadyApproved: | ||||
|         pass | ||||
| 
 | ||||
|       return make_response('', 201) | ||||
|  | @ -1153,8 +1058,11 @@ class SuperUserRepositoryBuildLogs(ApiResource): | |||
|     """ Return the build logs for the build specified by the build uuid. """ | ||||
|     if not SuperUserPermission().can(): | ||||
|       abort(403) | ||||
| 
 | ||||
|     return get_logs_or_log_url(model.build.get_repository_build(build_uuid)) | ||||
|     try: | ||||
|       repo_build = pre_oci_model.get_repository_build(build_uuid) | ||||
|       return get_logs_or_log_url(repo_build) | ||||
|     except InvalidRepositoryBuildException as e: | ||||
|       raise InvalidResponse(e.message) | ||||
| 
 | ||||
| 
 | ||||
| @resource('/v1/superuser/<build_uuid>/status') | ||||
|  | @ -1172,8 +1080,11 @@ class SuperUserRepositoryBuildStatus(ApiResource): | |||
|     """ Return the status for the builds specified by the build uuids. """ | ||||
|     if not SuperUserPermission().can(): | ||||
|       abort(403) | ||||
|     build = model.build.get_repository_build(build_uuid) | ||||
|     return build_status_view(build) | ||||
|     try: | ||||
|       build = pre_oci_model.get_repository_build(build_uuid) | ||||
|     except InvalidRepositoryBuildException as e: | ||||
|       raise InvalidResponse(e.message) | ||||
|     return build.to_dict() | ||||
| 
 | ||||
| 
 | ||||
| @resource('/v1/superuser/<build_uuid>/build') | ||||
|  | @ -1193,8 +1104,8 @@ class SuperUserRepositoryBuildResource(ApiResource): | |||
|       abort(403) | ||||
| 
 | ||||
|     try: | ||||
|       build = model.build.get_repository_build(build_uuid) | ||||
|     except model.build.InvalidRepositoryBuildException: | ||||
|       build = pre_oci_model.get_repository_build(build_uuid) | ||||
|     except InvalidRepositoryBuildException: | ||||
|       raise NotFound() | ||||
| 
 | ||||
|     return build_status_view(build) | ||||
|     return build.to_dict() | ||||
|  |  | |||
							
								
								
									
										443
									
								
								endpoints/api/superuser_models_interface.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								endpoints/api/superuser_models_interface.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,443 @@ | |||
| import json | ||||
| from abc import ABCMeta, abstractmethod | ||||
| from collections import namedtuple | ||||
| from datetime import datetime | ||||
| 
 | ||||
| from dateutil.relativedelta import relativedelta | ||||
| from six import add_metaclass | ||||
| from tzlocal import get_localzone | ||||
| 
 | ||||
| from app import avatar, superusers | ||||
| from buildtrigger.basehandler import BuildTriggerHandler | ||||
| from data import model | ||||
| from endpoints.api import format_date | ||||
| from util.morecollections import AttrDict | ||||
| 
 | ||||
| 
 | ||||
| def user_view(user): | ||||
|   return { | ||||
|     'name': user.username, | ||||
|     'kind': 'user', | ||||
|     'is_robot': user.robot, | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| class BuildTrigger( | ||||
|   namedtuple('BuildTrigger', ['uuid', 'service_name', 'pull_robot', 'can_read', 'can_admin', 'for_build'])): | ||||
|   """ | ||||
|   BuildTrigger represent a trigger that is associated with a build | ||||
|   :type uuid: string | ||||
|   :type service_name: string | ||||
|   :type pull_robot: User | ||||
|   :type can_read: boolean | ||||
|   :type can_admin: boolean | ||||
|   :type for_build: boolean | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     if not self.uuid: | ||||
|       return None | ||||
| 
 | ||||
|     build_trigger = BuildTriggerHandler.get_handler(self) | ||||
|     build_source = build_trigger.config.get('build_source') | ||||
| 
 | ||||
|     repo_url = build_trigger.get_repository_url() if build_source else None | ||||
|     can_read = self.can_read or self.can_admin | ||||
| 
 | ||||
|     trigger_data = { | ||||
|       'id': self.uuid, | ||||
|       'service': self.service_name, | ||||
|       'is_active': build_trigger.is_active(), | ||||
| 
 | ||||
|       'build_source': build_source if can_read else None, | ||||
|       'repository_url': repo_url if can_read else None, | ||||
| 
 | ||||
|       'config': build_trigger.config if self.can_admin else {}, | ||||
|       'can_invoke': self.can_admin, | ||||
|     } | ||||
| 
 | ||||
|     if not self.for_build and self.can_admin and self.pull_robot: | ||||
|       trigger_data['pull_robot'] = user_view(self.pull_robot) | ||||
| 
 | ||||
|     return trigger_data | ||||
| 
 | ||||
| 
 | ||||
| class RepositoryBuild(namedtuple('RepositoryBuild', | ||||
|                                  ['uuid', 'logs_archived', 'repository_namespace_user_username', 'repository_name', | ||||
|                                   'can_write', 'can_read', 'pull_robot', 'resource_key', 'trigger', 'display_name', | ||||
|                                   'started', 'job_config', 'phase', 'status', 'error', 'archive_url'])): | ||||
|   """ | ||||
|   RepositoryBuild represents a build associated with a repostiory | ||||
|   :type uuid: string | ||||
|   :type logs_archived: boolean | ||||
|   :type repository_namespace_user_username: string | ||||
|   :type repository_name: string | ||||
|   :type can_write: boolean | ||||
|   :type can_write: boolean | ||||
|   :type pull_robot: User | ||||
|   :type resource_key: string | ||||
|   :type trigger: Trigger | ||||
|   :type display_name: string | ||||
|   :type started: boolean | ||||
|   :type job_config: {Any -> Any} | ||||
|   :type phase: string | ||||
|   :type status: string | ||||
|   :type error: string | ||||
|   :type archive_url: string | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
| 
 | ||||
|     resp = { | ||||
|       'id': self.uuid, | ||||
|       'phase': self.phase, | ||||
|       'started': format_date(self.started), | ||||
|       'display_name': self.display_name, | ||||
|       'status': self.status or {}, | ||||
|       'subdirectory': self.job_config.get('build_subdir', ''), | ||||
|       'dockerfile_path': self.job_config.get('build_subdir', ''), | ||||
|       'context': self.job_config.get('context', ''), | ||||
|       'tags': self.job_config.get('docker_tags', []), | ||||
|       'manual_user': self.job_config.get('manual_user', None), | ||||
|       'is_writer': self.can_write, | ||||
|       'trigger': self.trigger.to_dict(), | ||||
|       'trigger_metadata': self.job_config.get('trigger_metadata', None) if self.can_read else None, | ||||
|       'resource_key': self.resource_key, | ||||
|       'pull_robot': user_view(self.pull_robot) if self.pull_robot else None, | ||||
|       'repository': { | ||||
|         'namespace': self.repository_namespace_user_username, | ||||
|         'name': self.repository_name | ||||
|       }, | ||||
|       'error': self.error, | ||||
|     } | ||||
| 
 | ||||
|     if self.can_write: | ||||
|       if self.resource_key is not None: | ||||
|         resp['archive_url'] = self.archive_url | ||||
|       elif self.job_config.get('archive_url', None): | ||||
|         resp['archive_url'] = self.job_config['archive_url'] | ||||
| 
 | ||||
|     return resp | ||||
| 
 | ||||
| 
 | ||||
| class Approval(namedtuple('Approval', ['approver', 'approval_type', 'approved_date', 'notes'])): | ||||
|   """ | ||||
|   Approval represents whether a key has been approved or not | ||||
|   :type approver: User | ||||
|   :type approval_type: string | ||||
|   :type approved_date: Date | ||||
|   :type notes: string | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     return { | ||||
|       'approver': self.approver.to_dict() if self.approver else None, | ||||
|       'approval_type': self.approval_type, | ||||
|       'approved_date': self.approved_date, | ||||
|       'notes': self.notes, | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| class ServiceKey(namedtuple('ServiceKey', ['name', 'kid', 'service', 'jwk', 'metadata', 'created_date', | ||||
|                                            'expiration_date', 'rotation_duration', 'approval'])): | ||||
|   """ | ||||
|   ServiceKey is an apostille signing key | ||||
|   :type name: string | ||||
|   :type kid: int | ||||
|   :type service: string | ||||
|   :type jwk: string | ||||
|   :type metadata: string | ||||
|   :type created_date: Date | ||||
|   :type expiration_date: Date | ||||
|   :type rotation_duration: Date | ||||
|   :type approval: Approval | ||||
| 
 | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     return { | ||||
|       'name': self.name, | ||||
|       'kid': self.kid, | ||||
|       'service': self.service, | ||||
|       'jwk': self.jwk, | ||||
|       'metadata': self.metadata, | ||||
|       'created_date': self.created_date, | ||||
|       'expiration_date': self.expiration_date, | ||||
|       'rotation_duration': self.rotation_duration, | ||||
|       'approval': self.approval.to_dict() if self.approval is not None else None, | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| class User(namedtuple('User', ['username', 'email', 'verified', 'enabled', 'robot'])): | ||||
|   """ | ||||
|   User represents a single user. | ||||
|   :type username: string | ||||
|   :type email: string | ||||
|   :type verified: boolean | ||||
|   :type enabled: boolean | ||||
|   :type robot: User | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     user_data = { | ||||
|       'kind': 'user', | ||||
|       'name': self.username, | ||||
|       'username': self.username, | ||||
|       'email': self.email, | ||||
|       'verified': self.verified, | ||||
|       'avatar': avatar.get_data_for_user(self), | ||||
|       'super_user': superusers.is_superuser(self.username), | ||||
|       'enabled': self.enabled, | ||||
|     } | ||||
| 
 | ||||
|     return user_data | ||||
| 
 | ||||
| 
 | ||||
| class Organization(namedtuple('Organization', ['username', 'email'])): | ||||
|   """ | ||||
|   Organization represents a single org. | ||||
|   :type username: string | ||||
|   :type email: string | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     return { | ||||
|       'name': self.username, | ||||
|       'email': self.email, | ||||
|       'avatar': avatar.get_data_for_org(self), | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| class LogEntry( | ||||
|   namedtuple('LogEntry', [ | ||||
|     'metadata_json', 'ip', 'datetime', 'performer_email', 'performer_username', 'performer_robot', | ||||
|     'account_organization', 'account_username', 'account_email', 'account_robot', 'kind', | ||||
|   ])): | ||||
|   """ | ||||
|   LogEntry a single log entry. | ||||
|   :type metadata_json: string | ||||
|   :type ip: string | ||||
|   :type datetime: string | ||||
|   :type performer_email: int | ||||
|   :type performer_username: string | ||||
|   :type performer_robot: boolean | ||||
|   :type account_organization: boolean | ||||
|   :type account_username: string | ||||
|   :type account_email: string | ||||
|   :type account_robot: boolean | ||||
|   :type kind_id: int | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     view = { | ||||
|       'kind': self.kind, | ||||
|       'metadata': json.loads(self.metadata_json), | ||||
|       'ip': self.ip, | ||||
|       'datetime': format_date(self.datetime), | ||||
|     } | ||||
| 
 | ||||
|     if self.performer_username: | ||||
|       performer = AttrDict({'username': self.performer_username, 'email': self.performer_email}) | ||||
|       performer.robot = None | ||||
|       if self.performer_robot: | ||||
|         performer.robot = self.performer_robot | ||||
| 
 | ||||
|       view['performer'] = { | ||||
|         'kind': 'user', | ||||
|         'name': self.performer_username, | ||||
|         'is_robot': self.performer_robot, | ||||
|         'avatar': avatar.get_data_for_user(performer), | ||||
|       } | ||||
| 
 | ||||
|     if self.account_username: | ||||
|       account = AttrDict({'username': self.account_username, 'email': self.account_email}) | ||||
|       if self.account_organization: | ||||
| 
 | ||||
|         view['namespace'] = { | ||||
|           'kind': 'org', | ||||
|           'name': self.account_username, | ||||
|           'avatar': avatar.get_data_for_org(account), | ||||
|         } | ||||
|       else: | ||||
|         account.robot = None | ||||
|         if self.account_robot: | ||||
|           account.robot = self.account_robot | ||||
|         view['namespace'] = { | ||||
|           'kind': 'user', | ||||
|           'name': self.account_username, | ||||
|           'avatar': avatar.get_data_for_user(account), | ||||
|         } | ||||
| 
 | ||||
|     return view | ||||
| 
 | ||||
| 
 | ||||
| class LogEntryPage( | ||||
|   namedtuple('LogEntryPage', ['logs', 'next_page_token'])): | ||||
|   """ | ||||
|   LogEntryPage represents a single page of logs. | ||||
|   :type logs: [LogEntry] | ||||
|   :type next_page_token: {any -> any} | ||||
|   """ | ||||
| 
 | ||||
| 
 | ||||
| class AggregatedLogEntry( | ||||
|   namedtuple('AggregatedLogEntry', ['count', 'kind_id', 'day', 'start_time'])): | ||||
|   """ | ||||
|   AggregatedLogEntry represents an aggregated view of logs. | ||||
|   :type count: int | ||||
|   :type kind_id: int | ||||
|   :type day: string | ||||
|   :type start_time: Date | ||||
|   """ | ||||
| 
 | ||||
|   def to_dict(self): | ||||
|     synthetic_date = datetime(self.start_time.year, self.start_time.month, int(self.day), tzinfo=get_localzone()) | ||||
|     if synthetic_date.day < self.start_time.day: | ||||
|       synthetic_date = synthetic_date + relativedelta(months=1) | ||||
|     kinds = model.log.get_log_entry_kinds() | ||||
|     view = { | ||||
|       'kind': kinds[self.kind_id], | ||||
|       'count': self.count, | ||||
|       'datetime': format_date(synthetic_date), | ||||
|     } | ||||
| 
 | ||||
|     return view | ||||
| 
 | ||||
| 
 | ||||
| @add_metaclass(ABCMeta) | ||||
| class SuperuserDataInterface(object): | ||||
|   """ | ||||
|   Interface that represents all data store interactions required by a superuser api. | ||||
|   """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_logs_query(self, start_time, end_time, page_token=None): | ||||
|     """ | ||||
|     Returns a LogEntryPage. | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_aggregated_logs(self, start_time, end_time): | ||||
|     """ | ||||
|     Returns a list of AggregatedLogEntry | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_organizations(self): | ||||
|     """ | ||||
|     Returns a list of Organization | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_active_users(self): | ||||
|     """ | ||||
|     Returns a list of User | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def create_install_user(self, username, password, email): | ||||
|     """ | ||||
|     Returns the created user and confirmation code for email confirmation | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_nonrobot_user(self, username): | ||||
|     """ | ||||
|     Returns a User | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def create_reset_password_email_code(self, email): | ||||
|     """ | ||||
|     Returns a recover password code | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def delete_user(self, username): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def change_password(self, username, password): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def update_email(self, username, email, auto_verify): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def update_enabled(self, username, enabled): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def take_ownership(self, namespace, authed_user): | ||||
|     """ | ||||
|     Returns id of entity and whether the entity was a user | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def delete_organization(self, name): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def change_organization_name(self, old_org_name, new_org_name): | ||||
|     """ | ||||
|     Returns updated Organization | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def list_all_service_keys(self): | ||||
|     """ | ||||
|     Returns a list of service keys | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def generate_service_key(self, service, expiration_date, kid=None, name='', metadata=None, rotation_duration=None): | ||||
|     """ | ||||
|     Returns a tuple of private key and public key id | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def approve_service_key(self, kid, approver, approval_type, notes=''): | ||||
|     """ | ||||
|     Returns the approved Key | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_service_key(self, kid, service=None, alive_only=True, approved_only=True): | ||||
|     """ | ||||
|     Returns ServiceKey | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def set_key_expiration(self, kid, expiration_date): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def update_service_key(self, kid, name=None, metadata=None): | ||||
|     """ | ||||
|     Returns None | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def delete_service_key(self, kid): | ||||
|     """ | ||||
|     Returns deleted ServiceKey | ||||
|     """ | ||||
| 
 | ||||
|   @abstractmethod | ||||
|   def get_repository_build(self, uuid): | ||||
|     """ | ||||
|     Returns RepositoryBuild | ||||
|     """ | ||||
							
								
								
									
										215
									
								
								endpoints/api/superuser_models_pre_oci.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										215
									
								
								endpoints/api/superuser_models_pre_oci.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,215 @@ | |||
| import features | ||||
| from app import all_queues, userfiles | ||||
| from auth.permissions import ReadRepositoryPermission, ModifyRepositoryPermission, AdministerRepositoryPermission | ||||
| from data import model, database | ||||
| from endpoints.api.build import get_job_config, _get_build_status | ||||
| from endpoints.api.superuser_models_interface import BuildTrigger | ||||
| from endpoints.api.superuser_models_interface import SuperuserDataInterface, LogEntryPage, LogEntry, Organization, User, \ | ||||
|   ServiceKey, Approval, RepositoryBuild, AggregatedLogEntry | ||||
| 
 | ||||
| 
 | ||||
| def _create_log(log, log_kind): | ||||
|   account_organization = None | ||||
|   account_username = None | ||||
|   account_email = None | ||||
|   account_robot = None | ||||
|   try: | ||||
|     account_organization = log.account.organization | ||||
|     account_username = log.account.username | ||||
|     account_email = log.account.email | ||||
|     account_robot = log.account.robot | ||||
|   except AttributeError: | ||||
|     pass | ||||
| 
 | ||||
|   performer_robot = None | ||||
|   performer_username = None | ||||
|   performer_email = None | ||||
| 
 | ||||
|   try: | ||||
|     performer_robot = log.performer.robot | ||||
|     performer_username = log.performer.username | ||||
|     performer_email = log.performer.email | ||||
|   except AttributeError: | ||||
|     pass | ||||
| 
 | ||||
|   return LogEntry(log.metadata_json, log.ip, log.datetime, performer_email, performer_username, | ||||
|                   performer_robot, account_organization, account_username, | ||||
|                   account_email, account_robot, log_kind[log.kind_id]) | ||||
| 
 | ||||
| 
 | ||||
| def _create_user(user): | ||||
|   return User(user.username, user.email, user.verified, user.enabled, user.robot) | ||||
| 
 | ||||
| 
 | ||||
| def _create_key(key): | ||||
|   approval = None | ||||
|   if key.approval is not None: | ||||
|     approval = Approval(_create_user(key.approval.approver), key.approval.approval_type, key.approval.approved_date, | ||||
|                         key.approval.notes) | ||||
| 
 | ||||
|   return ServiceKey(key.name, key.kid, key.service, key.jwk, key.metadata, key.created_date, key.expiration_date, | ||||
|                     key.rotation_duration, approval) | ||||
| 
 | ||||
| 
 | ||||
| class ServiceKeyDoesNotExist(Exception): | ||||
|   pass | ||||
| 
 | ||||
| 
 | ||||
| class ServiceKeyAlreadyApproved(Exception): | ||||
|   pass | ||||
| 
 | ||||
| 
 | ||||
| class InvalidRepositoryBuildException(Exception): | ||||
|   pass | ||||
| 
 | ||||
| 
 | ||||
| class PreOCIModel(SuperuserDataInterface): | ||||
|   """ | ||||
|   PreOCIModel implements the data model for the SuperUser using a database schema | ||||
|   before it was changed to support the OCI specification. | ||||
|   """ | ||||
| 
 | ||||
|   def get_repository_build(self, uuid): | ||||
|     try: | ||||
|       build = model.build.get_repository_build(uuid) | ||||
|     except model.InvalidRepositoryBuildException as e: | ||||
|       raise InvalidRepositoryBuildException(e.message) | ||||
| 
 | ||||
|     repo_namespace = build.repository_namespace_user_username | ||||
|     repo_name = build.repository_name | ||||
| 
 | ||||
|     can_read = ReadRepositoryPermission(repo_namespace, repo_name).can() | ||||
|     can_write = ModifyRepositoryPermission(repo_namespace, repo_name).can() | ||||
|     can_admin = AdministerRepositoryPermission(repo_namespace, repo_name).can() | ||||
|     job_config = get_job_config(build.job_config) | ||||
|     phase, status, error = _get_build_status(build) | ||||
|     url = userfiles.get_file_url(self.resource_key, requires_cors=True) | ||||
| 
 | ||||
|     return RepositoryBuild(build.uuid, build.logs_archived, repo_namespace, repo_name, can_write, can_read, | ||||
|                            _create_user(build.pull_robot), build.resource_key, | ||||
|                            BuildTrigger(build.trigger.uuid, build.trigger.service.name, | ||||
|                                         _create_user(build.trigger.pull_robot), can_read, can_admin, True), | ||||
|                            build.display_name, build.display_name, build.started, job_config, phase, status, error, url) | ||||
| 
 | ||||
|   def delete_service_key(self, kid): | ||||
|     try: | ||||
|       key = model.service_keys.delete_service_key(kid) | ||||
|     except model.ServiceKeyDoesNotExist: | ||||
|       raise ServiceKeyDoesNotExist | ||||
|     return _create_key(key) | ||||
| 
 | ||||
|   def update_service_key(self, kid, name=None, metadata=None): | ||||
|     model.service_keys.update_service_key(kid, name, metadata) | ||||
| 
 | ||||
|   def set_key_expiration(self, kid, expiration_date): | ||||
|     model.service_keys.set_key_expiration(kid, expiration_date) | ||||
| 
 | ||||
|   def get_service_key(self, kid, service=None, alive_only=True, approved_only=True): | ||||
|     try: | ||||
|       key = model.service_keys.get_service_key(kid, approved_only=approved_only, alive_only=alive_only) | ||||
|       return _create_key(key) | ||||
|     except model.ServiceKeyDoesNotExist: | ||||
|       raise ServiceKeyDoesNotExist | ||||
| 
 | ||||
|   def approve_service_key(self, kid, approver, approval_type, notes=''): | ||||
|     try: | ||||
|       key = model.service_keys.approve_service_key(kid, approver, approval_type, notes=notes) | ||||
|       return _create_key(key) | ||||
|     except model.ServiceKeyDoesNotExist: | ||||
|       raise ServiceKeyDoesNotExist | ||||
|     except model.ServiceKeyAlreadyApproved: | ||||
|       raise ServiceKeyAlreadyApproved | ||||
| 
 | ||||
|   def generate_service_key(self, service, expiration_date, kid=None, name='', metadata=None, rotation_duration=None): | ||||
|     (private_key, key) = model.service_keys.generate_service_key(service, expiration_date, metadata=metadata, name=name) | ||||
| 
 | ||||
|     return private_key, key.kid | ||||
| 
 | ||||
|   def list_all_service_keys(self): | ||||
|     keys = model.service_keys.list_all_keys() | ||||
|     return [_create_key(key) for key in keys] | ||||
| 
 | ||||
|   def change_organization_name(self, old_org_name, new_org_name): | ||||
|     org = model.organization.get_organization(old_org_name) | ||||
|     if new_org_name is not None: | ||||
|       org = model.user.change_username(org.id, new_org_name) | ||||
| 
 | ||||
|     return Organization(org.username, org.email) | ||||
| 
 | ||||
|   def delete_organization(self, name): | ||||
|     org = model.organization.get_organization(name) | ||||
|     model.user.delete_user(org, all_queues) | ||||
| 
 | ||||
|   def take_ownership(self, namespace, authed_user): | ||||
|     entity = model.user.get_user_or_org(namespace) | ||||
|     if entity is None: | ||||
|       return None, False | ||||
| 
 | ||||
|     was_user = not entity.organization | ||||
|     if entity.organization: | ||||
|       # Add the superuser as an admin to the owners team of the org. | ||||
|       model.organization.add_user_as_admin(authed_user, entity) | ||||
|     else: | ||||
|       # If the entity is a user, convert it to an organization and add the current superuser | ||||
|       # as the admin. | ||||
|       model.organization.convert_user_to_organization(entity, authed_user) | ||||
|     return entity.id, was_user | ||||
| 
 | ||||
|   def update_enabled(self, username, enabled): | ||||
|     user = model.user.get_nonrobot_user(username) | ||||
|     model.user.update_enabled(user, bool(enabled)) | ||||
| 
 | ||||
|   def update_email(self, username, email, auto_verify): | ||||
|     user = model.user.get_nonrobot_user(username) | ||||
|     model.user.update_email(user, email, auto_verify) | ||||
| 
 | ||||
|   def change_password(self, username, password): | ||||
|     user = model.user.get_nonrobot_user(username) | ||||
|     model.user.change_password(user, password) | ||||
| 
 | ||||
|   def delete_user(self, username): | ||||
|     user = model.user.get_nonrobot_user(username) | ||||
|     model.user.delete_user(user, all_queues, force=True) | ||||
| 
 | ||||
|   def create_reset_password_email_code(self, email): | ||||
|     code = model.user.create_reset_password_email_code(email) | ||||
|     return code.code | ||||
| 
 | ||||
|   def get_nonrobot_user(self, username): | ||||
|     user = model.user.get_nonrobot_user(username) | ||||
|     if user is None: | ||||
|       return None | ||||
|     return _create_user(user) | ||||
| 
 | ||||
|   def create_install_user(self, username, password, email): | ||||
|     prompts = model.user.get_default_user_prompts(features) | ||||
|     user = model.user.create_user(username, password, email, auto_verify=not features.MAILING, | ||||
|                                   email_required=features.MAILING, prompts=prompts) | ||||
| 
 | ||||
|     return_user = _create_user(user) | ||||
|     # If mailing is turned on, send the user a verification email. | ||||
|     if features.MAILING: | ||||
|       confirmation = model.user.create_confirm_email_code(user) | ||||
|       return return_user, confirmation.code | ||||
|     return return_user, '' | ||||
| 
 | ||||
|   def get_active_users(self): | ||||
|     users = model.user.get_active_users() | ||||
|     return [_create_user(user) for user in users] | ||||
| 
 | ||||
|   def get_organizations(self): | ||||
|     return [Organization(org.username, org.email) for org in model.organization.get_organizations()] | ||||
| 
 | ||||
|   def get_aggregated_logs(self, start_time, end_time): | ||||
|     aggregated_logs = model.log.get_aggregated_logs(start_time, end_time) | ||||
|     return [AggregatedLogEntry(log.count, log.kind_id, log.day, start_time) for log in aggregated_logs] | ||||
| 
 | ||||
|   def get_logs_query(self, start_time, end_time, page_token=None): | ||||
|     logs_query = model.log.get_logs_query(start_time, end_time) | ||||
|     logs, next_page_token = model.modelutil.paginate(logs_query, database.LogEntry, descending=True, | ||||
|                                                      page_token=page_token, limit=20) | ||||
|     kinds = model.log.get_log_entry_kinds() | ||||
|     return LogEntryPage([_create_log(log, kinds) for log in logs], next_page_token) | ||||
| 
 | ||||
| 
 | ||||
| pre_oci_model = PreOCIModel() | ||||
		Reference in a new issue