def created(self, model: IAMPolicy): if model.name == "system": return project = Project.get(model.name) if project is None: model.delete() return needs_save = False for binding in list(model.bindings): role_name = binding['role'] if project is not None: role = IAMProjectRole.get(project, role_name) else: role = IAMSystemRole.get(role_name) if role is None: needs_save = True model.bindings.remove(binding) # TODO: do we remove service accounts that no longer exist? # TODO: do we remove groups that no longer exist? if needs_save: model.save()
def enforce_permission(self, permission, project=None): if len(self.system_roles) > 0: if permission in [p['name'] for p in SYSTEM_PERMISSIONS]: for role_name in self.system_roles: role = IAMSystemRole.get(role_name) if role is not None and permission in role.permissions: return raise cherrypy.HTTPError( 403, "Insufficient permissions (%s) to perform the requested action." % permission) if project is not None: project_policy = IAMPolicy.get(project.name) if project_policy is None: raise cherrypy.HTTPError( 500, "Could not find iam policy document for project %s" % project.name) project_roles = self.find_roles(project_policy) for role_name in project_roles: role = IAMProjectRole.get(project, role_name) if role is not None and permission in role.permissions: return raise cherrypy.HTTPError( 403, "Insufficient permissions (%s) to perform the " "requested action in the project %s." % (permission, project.name)) raise cherrypy.HTTPError( 403, "Insufficient permissions (%s) to perform the requested action." % permission)
def get_projects(self) -> List[Project]: projects = [] policies = IAMPolicy.list() for policy in policies: if policy.name == "system": continue if len(self.find_roles(policy)) > 0: project = Project.get(policy.name) if project is not None: projects.append(project) return projects
def get(self): """Get a system policy --- get: description: Get a system policy tags: - iam - policy responses: 200: description: The policy """ return ResponsePolicy.from_database(IAMPolicy.get("system"))
def get(self): """Get a project policy --- get: description: Get a project policy tags: - iam - policy responses: 200: description: The policy """ return ResponsePolicy.from_database( IAMPolicy.get(cherrypy.request.project.name))
def create(self): """Create a project --- post: description: Create a project tags: - iam - project requestBody: description: Project to create responses: 200: description: The created project """ request: RequestCreateProject = cherrypy.request.model project = Project.get(request.name) if project is not None: raise cherrypy.HTTPError(409, 'A project with the requested name already exists.') if request.name == "system": raise cherrypy.HTTPError(409, 'Cannot use a reserved name as the project name') project = Project() project.name = request.name project.create() IAMProjectRole.create_default_roles(project) ProjectServiceAccount.create_default_service_account(project) quota = ProjectQuota() quota.name = project.name quota.create() IAMPolicy.create_project_policy(project, cherrypy.request.token) return ResponseProject.from_database(project)
def run(self, args) -> int: cache_client.connect(url=args.redis_url) if args.kube_config != "" or args.kube_master != "": self.logger.info("Using kube-config configuration") Configuration.set_default(Configuration()) if args.kube_config != "": config.load_kube_config(config_file=args.kube_config) if args.kube_master != "": Configuration._default.host = args.kube_master else: self.logger.info("Using in-cluster configuration") config.load_incluster_config() while True: try: client.CoreV1Api().list_namespace() break except urllib3.exceptions.HTTPError as e: self.logger.error("Error connecting to the Kubernetes API. Trying again in 5 seconds. Error: " + str(e)) time.sleep(5) old_json_encoder = json.JSONEncoder.default def json_encoder(self, o): # pragma: no cover if isinstance(o, uuid.UUID): return str(o) if isinstance(o, arrow.Arrow): return o.isoformat() if isinstance(o, ipaddress.IPv4Network): return str(o) if isinstance(o, ipaddress.IPv4Address): return str(o) if isinstance(o, enum.Enum): return o.value if isinstance(o, datetime.datetime): return str(o.isoformat()) return old_json_encoder(self, o) json.JSONEncoder.default = json_encoder self.logger.info("Creating CRDs") IAMSystemRole.create_crd() IAMSystemRole.wait_for_crd() IAMProjectRole.create_crd() IAMProjectRole.wait_for_crd() IAMPolicy.create_crd() IAMPolicy.wait_for_crd() IAMPolicy.create_system_policy() SystemServiceAccount.create_crd() SystemServiceAccount.wait_for_crd() ProjectServiceAccount.create_crd() ProjectServiceAccount.wait_for_crd() IAMSystemRole.create_default_roles() SystemServiceAccount.create_admin_sa() ProjectQuota.create_crd() ProjectQuota.wait_for_crd() Region.create_crd() Region.wait_for_crd() Zone.create_crd() Zone.wait_for_crd() Network.create_crd() Network.wait_for_crd() NetworkPort.create_crd() NetworkPort.wait_for_crd() Image.create_crd() Image.wait_for_crd() Flavor.create_crd() Flavor.wait_for_crd() Volume.create_crd() Volume.wait_for_crd() Instance.create_crd() Instance.wait_for_crd() Keypair.create_crd() Keypair.wait_for_crd() self.logger.info("CRDs have been created") self.menu_url = args.menu_url self.vmware = VMWare(args.vcenter_host, args.vcenter_port, args.vcenter_username, args.vcenter_password) self.leader_elector = LeaderElector("sandwich-controller", "kube-system", self.on_started_leading, self.on_stopped_leading) self.leader_elector.start() return 0
def unmarshal(cls, token_string, fernet): token = cls() try: token_data_bytes = fernet.decrypt(token_string.encode()) token_json = json.loads(token_data_bytes.decode()) token.expires_at = arrow.get( token_json['expires_at'] ) if token_json['expires_at'] is not None else None if token.expires_at is not None and token.expires_at <= arrow.now( 'UTC'): raise cherrypy.HTTPError(401, 'Invalid Authorization Token.') token.metadata = token_json['metadata'] token.email = token_json['email'] except InvalidToken: try: token_payload = jwt.decode( token_string, token.get_oauth_rsa_key( jwt.get_unverified_header(token_string)), algorithms=['RS256'], audience=settings.OPENID_CLIENT_ID, issuer=settings.OPENID_ISSUER_URL) token.expires_at = arrow.get(token_payload['exp']) token.email = token_payload[settings.OPENID_EMAIL_CLAIM] token.oauth_groups = token_payload[ settings.OPENID_GROUPS_CLAIM] except jose.JOSEError: # Unable to decode jwt raise cherrypy.HTTPError(401, 'Invalid Authorization Token.') username, domain, *_ = token.email.split("@") if domain.endswith('sandwich.local'): type, project_name, *_ = domain.split('.') system = True if project_name == 'system' else False project = None if system is False: project = Project.get(project_name) if project is None: # Email domain contains invalid project raise cherrypy.HTTPError(401, 'Invalid Authorization Token.') if type == 'service-account': if system: service_account = SystemServiceAccount.get(username) else: service_account = ProjectServiceAccount.get( project, username) if service_account is None: raise cherrypy.HTTPError(401, 'Invalid Authorization Token.') if token.metadata['key'] not in service_account.keys: if 'instance' in token.metadata: if Instance.get(project, token.metadata['instance']) is None: # Token says it's from an instance but we can't find it raise cherrypy.HTTPError( 401, 'Invalid Authorization Token.') else: # Token says it's a service account key but it doesn't exist raise cherrypy.HTTPError( 401, 'Invalid Authorization Token.') else: expire_at = service_account.keys[token.metadata['key']] if expire_at <= arrow.now('UTC'): # Service account key is expired raise cherrypy.HTTPError( 401, 'Invalid Authorization Token.') token.service_account = service_account else: # Invalid email type raise cherrypy.HTTPError(401, 'Invalid Authorization Token.') system_policy = IAMPolicy.get("system") token.system_roles = token.find_roles(system_policy) return token
def set(self): """Set a system policy --- post: description: Set a system policy tags: - iam - policy requestBody: description: The policy responses: 200: description: The policy """ cherrypy.response.status = 204 policy = IAMPolicy.get("system") bindings = [] request: RequestSetPolicy = cherrypy.request.model if request.resource_version != policy.resource_version: raise cherrypy.HTTPError( 400, 'The policy has a newer resource version than requested') has_one_admin = False for binding in request.bindings: role = IAMSystemRole.get(binding.role) if role is None: raise cherrypy.HTTPError(404, 'Unknown system role ' + binding.role) if role.name == 'admin': if len(binding.members) > 0: has_one_admin = True for member in binding.members: kind, email = member.split(":") user, domain = email.split('@') if kind == 'group': group = IAMSystemGroup.get(user) if group is None: raise cherrypy.HTTPError(404, 'Unknown Group ' + email) if kind == 'serviceAccount': project = domain.split('.')[1] if project != 'system': raise cherrypy.HTTPError( 400, 'Can only add system service accounts to a system policy.' ) sa = SystemServiceAccount.get(user) if sa is None: raise cherrypy.HTTPError( 404, 'Unknown service account ' + email) bindings.append(binding.to_native()) if has_one_admin is False: raise cherrypy.HTTPError( 400, 'Must have an admin binding with at least one member') policy.bindings = bindings policy.save()
def set(self): """Set a project policy --- post: description: Set a project policy tags: - iam - policy requestBody: description: The policy responses: 200: description: The policy """ cherrypy.response.status = 204 project: Project = cherrypy.request.project policy = IAMPolicy.get(project) bindings = [] request: RequestSetPolicy = cherrypy.request.model if request.resource_version != policy.resource_version: raise cherrypy.HTTPError( 400, 'The policy has a newer resource version than requested') has_one_owner = False for binding in request.bindings: role = IAMProjectRole.get(project, binding.role) if role is None: raise cherrypy.HTTPError( 404, 'Unknown project role ' + binding.role) if role.name == "owner": if len(binding.members) > 0: has_one_owner = True for member in binding.members: kind, email = member.split(":") user, domain = email.split('@') if kind == 'group': group = IAMSystemGroup.get(user) if group is None: raise cherrypy.HTTPError(404, 'Unknown Group ' + email) if kind == 'serviceAccount': sa_project_name = domain.split('.')[1] if sa_project_name == 'system': sa = SystemServiceAccount.get(user) else: sa_project = Project.get(sa_project_name) if sa_project is None: raise cherrypy.HTTPError( 404, 'Unknown service account ' + email) sa = ProjectServiceAccount.get(sa_project, user) if sa is None: raise cherrypy.HTTPError( 404, 'Unknown service account ' + email) bindings.append(binding.to_native()) if has_one_owner is False: raise cherrypy.HTTPError( 400, 'Must have a owner binding with at least one member') policy.bindings = bindings policy.save()