예제 #1
0
    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()
예제 #2
0
파일: token.py 프로젝트: sandwichcloud/deli
    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)
예제 #3
0
파일: token.py 프로젝트: sandwichcloud/deli
    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
예제 #4
0
 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"))
예제 #5
0
 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))
예제 #6
0
    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)
예제 #7
0
파일: run.py 프로젝트: sandwichcloud/deli
    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
예제 #8
0
파일: token.py 프로젝트: sandwichcloud/deli
    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
예제 #9
0
    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()
예제 #10
0
    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()