Exemplo n.º 1
0
class MocProjects(base.MocBaseApi):
    """
    API Endpoint for applying, or listing accessible projects.
    """

    url = r'^moc/Projects/?$'
    task_type = 'moc_create_project'

    config_group = config_groups.DynamicNameConfigGroup(
        children=[
            config_fields.BoolConfig(
                'create_default_network',
                help_text='If set to faulse, overrides what is in setup_network.',
                default=True,
                sample_default=True,
            ),
            config_fields.StrConfig(
                'region',
                help_text='Region for creating default network and quota.',
                default='RegionOne',
                sample_default='RegionOne'
            ),
            config_fields.StrConfig(
                "project_domain_id",
                help_text="Domain id for projects.",
                default="default",
                sample_default="Default"
            ),
        ]
    )

    @utils.authenticated
    def post(self, request, format=None):
        request.data['email'] = request.keystone_user['username']
        request.data['region'] = self.config['region']
        request.data['domain_id'] = self.config['project_domain_id']

        if not self.config['create_default_network']:
            request.data['setup_network'] = False

        if 'project_name' not in request.data:
            message = 'Missing project_name in request.'
            self.logger.info(message)
            return self.response(message, 400)

        project = self.identity.find_project(
            request.data['project_name'], self.config.project_domain_id)
        if project:
            message = ('Project %s already exists.'
                       % request.data['project_name'])
            self.logger.info(message)
            return self.response_error(message, 409)

        return self.create_task(request)

    @utils.authenticated
    def get(self, request, format=None):
        # List other projects that a user has access to, or has applied for.
        pass
Exemplo n.º 2
0
class CreateProjectAndUser(BaseDelegateAPI):

    url = r"^actions/CreateProjectAndUser/?$"

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.StrConfig(
                "default_region",
                help_text="Default region in which any potential resources may be created.",
                required=True,
                default="RegionOne",
            ),
            fields.StrConfig(
                "default_domain_id",
                help_text="Domain in which project and users will be created.",
                default="default",
                required=True,
            ),
            fields.StrConfig(
                "default_parent_id",
                help_text="Parent id under which this project will be created. "
                "Default is None, and will create under default domain.",
                default=None,
            ),
        ]
    )

    task_type = "create_project_and_user"

    def post(self, request, format=None):
        """
        Unauthenticated endpoint bound primarily to NewProjectWithUser.

        This process requires approval, so this will validate
        incoming data and create a task to be approved
        later.
        """
        self.logger.info("(%s) - Starting new project task." % timezone.now())

        class_conf = self.config

        # we need to set the region the resources will be created in:
        request.data["region"] = class_conf.default_region

        # domain
        request.data["domain_id"] = class_conf.default_domain_id

        # parent_id for new project, if null defaults to domain:
        request.data["parent_id"] = class_conf.default_parent_id

        self.task_manager.create_from_request(self.task_type, request)

        return Response({"notes": ["task created"]}, status=202)
Exemplo n.º 3
0
class MocInviteUserAction(base.MocBaseAction):

    required = [
        'email',
        'project_id',
        'roles',
    ]

    serializer = serializers.MocInviteUserSerializer

    config_group = config_groups.DynamicNameConfigGroup(children=[
        config_fields.StrConfig("user_domain_id",
                                help_text="Domain to create projects in.",
                                default="default",
                                sample_default="Default")
    ])

    def _get_email(self):
        """This is the email where the invitation will be sent."""
        return self.email

    def _prepare(self):
        if not self._validate():
            self.add_note('Validation failed at _prepare')
            return

        self.action.auto_approve = True
        self.action.state = "pending"
        self.action.need_token = True
        self.set_token_fields(["confirm", "user"])

    def validate_token(self, token_data):
        if not self.valid or not token_data.get('confirm'):
            self.add_note('Invitation not valid or not accepted.')
            return False
        return True

    def write_to_approve_journal(self):
        pass

    def write_to_submit_journal(self, token_data):
        project = self.leader_identity.get_project(self.project_id)
        project_ref = {'name': project.name, 'domain_id': project.domain_id}
        services = self.find_services_for_project(project)
        self.submit_journal.append(
            operations.AddUserToProjectOperation(services, token_data['user'],
                                                 project_ref, self.roles))
Exemplo n.º 4
0
class MocHealthCheck(base.MocBaseApi):
    """
    API Endpoint for checking the health of Adjutant.
    """
    url = r'^moc/HealthCheck/?$'

    config_group = config_groups.DynamicNameConfigGroup(
        children=[
            config_fields.BoolConfig(
                'check_mailing_list',
                help_text='Check SSH connection to mailing list server.',
                default=True,
                sample_default=True,
            ),
        ]
    )

    def get(self, request, format=None):
        errors = {
            'MailingList': self._check_mailing_list()
        }

        return Response({'errors': errors})

    def _get_action_defaults(self, action):
        return CONF.workflow.action_defaults.get(action)

    def _check_mailing_list(self):
        config = self._get_action_defaults('MailingListSubscribeAction')
        try:
            with mailing_list.Mailman(config.host,
                                      config.port,
                                      config.user,
                                      config.private_key) as mailman:
                mailman.is_already_subscribed('*****@*****.**', config.list)
                return []
        except Exception as e:
            return [str(e)]
Exemplo n.º 5
0
class NewDefaultNetworkAction(BaseAction, ProjectMixin):
    """
    This action will setup all required basic networking
    resources so that a new user can launch instances
    right away.
    """

    required = [
        "setup_network",
        "project_id",
        "region",
    ]

    serializer = serializers.NewDefaultNetworkSerializer

    config_group = groups.DynamicNameConfigGroup(
        children=[
            groups.ConfigGroup(
                "region_defaults",
                children=[
                    fields.StrConfig(
                        "network_name",
                        help_text="Name to be given to the default network.",
                        default="default_network",
                    ),
                    fields.StrConfig(
                        "subnet_name",
                        help_text="Name to be given to the default subnet.",
                        default="default_subnet",
                    ),
                    fields.StrConfig(
                        "router_name",
                        help_text="Name to be given to the default router.",
                        default="default_router",
                    ),
                    fields.StrConfig(
                        "public_network",
                        help_text="ID of the public network.",
                    ),
                    fields.StrConfig(
                        "subnet_cidr",
                        help_text="CIDR for the default subnet.",
                    ),
                    fields.ListConfig(
                        "dns_nameservers",
                        help_text="DNS nameservers for the subnet.",
                    ),
                ],
            ),
            fields.DictConfig(
                "regions",
                help_text="Specific per region config for default network. "
                "See 'region_defaults'.",
                default={},
            ),
        ]
    )

    def __init__(self, *args, **kwargs):
        super(NewDefaultNetworkAction, self).__init__(*args, **kwargs)

    def _validate_region(self):
        if not self.region:
            self.add_note("ERROR: No region given.")
            return False

        id_manager = user_store.IdentityManager()
        region = id_manager.get_region(self.region)
        if not region:
            self.add_note("ERROR: Region does not exist.")
            return False

        self.add_note("Region: %s exists." % self.region)
        return True

    def _validate(self):
        self.action.valid = validate_steps(
            [
                self._validate_region,
                self._validate_project_id,
                self._validate_keystone_user_project_id,
            ]
        )
        self.action.save()

    def _create_network(self):
        neutron = openstack_clients.get_neutronclient(region=self.region)
        try:
            region_config = self.config.regions[self.region]
            network_config = self.config.region_defaults.overlay(region_config)
        except KeyError:
            network_config = self.config.region_defaults

        if not self.get_cache("network_id"):
            try:
                network_body = {
                    "network": {
                        "name": network_config.network_name,
                        "tenant_id": self.project_id,
                        "admin_state_up": True,
                    }
                }
                network = neutron.create_network(body=network_body)
            except Exception as e:
                self.add_note(
                    "Error: '%s' while creating network: %s"
                    % (e, network_config.network_name)
                )
                raise
            self.set_cache("network_id", network["network"]["id"])
            self.add_note(
                "Network %s created for project %s"
                % (network_config.network_name, self.project_id)
            )
        else:
            self.add_note(
                "Network %s already created for project %s"
                % (network_config.network_name, self.project_id)
            )

        if not self.get_cache("subnet_id"):
            try:
                subnet_body = {
                    "subnet": {
                        "network_id": self.get_cache("network_id"),
                        "ip_version": 4,
                        "tenant_id": self.project_id,
                        "dns_nameservers": network_config.dns_nameservers,
                        "cidr": network_config.subnet_cidr,
                    }
                }
                subnet = neutron.create_subnet(body=subnet_body)
            except Exception as e:
                self.add_note("Error: '%s' while creating subnet" % e)
                raise
            self.set_cache("subnet_id", subnet["subnet"]["id"])
            self.add_note("Subnet created for network %s" % network_config.network_name)
        else:
            self.add_note(
                "Subnet already created for network %s" % network_config.network_name
            )

        if not self.get_cache("router_id"):
            try:
                router_body = {
                    "router": {
                        "name": network_config.router_name,
                        "external_gateway_info": {
                            "network_id": network_config.public_network
                        },
                        "tenant_id": self.project_id,
                        "admin_state_up": True,
                    }
                }
                router = neutron.create_router(body=router_body)
            except Exception as e:
                self.add_note(
                    "Error: '%s' while creating router: %s"
                    % (e, network_config.router_name)
                )
                raise
            self.set_cache("router_id", router["router"]["id"])
            self.add_note("Router created for project %s" % self.project_id)
        else:
            self.add_note("Router already created for project %s" % self.project_id)

        if not self.get_cache("port_id"):
            try:
                interface_body = {"subnet_id": self.get_cache("subnet_id")}
                interface = neutron.add_interface_router(
                    self.get_cache("router_id"), body=interface_body
                )
            except Exception as e:
                self.add_note("Error: '%s' while attaching interface" % e)
                raise
            self.set_cache("port_id", interface["port_id"])
            self.add_note("Interface added to router for subnet")
        else:
            self.add_note("Interface added to router for project %s" % self.project_id)

    def _prepare(self):
        # Note: Do we need to get this from cache? it is a required setting
        # self.project_id = self.action.task.cache.get('project_id', None)
        self._validate()

    def _approve(self):
        self._validate()

        if self.setup_network and self.valid:
            self._create_network()

    def _submit(self, token_data, keystone_user=None):
        pass
Exemplo n.º 6
0
class UpdateProjectQuotasAction(BaseAction, QuotaMixin):
    """ Updates quota for a project to a given size in a list of regions """

    required = [
        "size",
        "project_id",
        "regions",
    ]

    serializer = serializers.UpdateProjectQuotasSerializer

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.FloatConfig(
                "size_difference_threshold",
                help_text="Precentage different allowed when matching quota sizes.",
                default=0.1,
                min=0,
                max=1,
            ),
            fields.IntConfig(
                "days_between_autoapprove",
                help_text="The allowed number of days between auto approved quota changes.",
                default=30,
            ),
        ]
    )

    def _get_email(self):

        if CONF.identity.username_is_email:
            return self.action.task.keystone_user["username"]
        else:
            id_manager = user_store.IdentityManager()
            user = id_manager.users.get(self.keystone_user["user_id"])
            email = user.email
            if email:
                return email

        self.add_note("User email address not set.")
        return None

    def _validate_quota_size_exists(self):
        size_list = CONF.quota.sizes.keys()
        if self.size not in size_list:
            self.add_note("Quota size: %s does not exist" % self.size)
            return False
        return True

    def _set_region_quota(self, region_name, quota_size):
        # Set the quota for an individual region
        quota_config = CONF.quota.sizes.get(quota_size, {})
        if not quota_config:
            self.add_note(
                "Project quota not defined for size '%s' in region %s."
                % (quota_size, region_name)
            )
            return

        quota_manager = QuotaManager(
            self.project_id, self.config.size_difference_threshold
        )

        quota_manager.set_region_quota(region_name, quota_config)

        self.add_note(
            "Project quota for region %s set to %s" % (region_name, quota_size)
        )

    def _can_auto_approve(self):
        wait_days = self.config.days_between_autoapprove
        task_list = models.Task.objects.filter(
            completed_on__gte=timezone.now() - timedelta(days=wait_days),
            task_type__exact=self.action.task.task_type,
            cancelled__exact=False,
            project_id__exact=self.project_id,
        )

        changed_in_period = False
        # Check to see if there have been any updates in the relavent regions
        # recently
        for task in task_list:
            for action in task.actions:
                intersect = set(action.action_data["regions"]).intersection(
                    self.regions
                )
                if intersect:
                    changed_in_period = True

        region_sizes = []

        quota_manager = QuotaManager(
            self.project_id, self.config.size_difference_threshold
        )

        for region in self.regions:
            current_size = quota_manager.get_region_quota_data(
                region, include_usage=False
            )["current_quota_size"]
            region_sizes.append(current_size)
            self.add_note(
                "Project has size '%s' in region: '%s'" % (current_size, region)
            )

        # Check for preapproved_quotas
        preapproved_quotas = []
        smaller_quotas = []

        # If all region sizes are the same
        if region_sizes.count(region_sizes[0]) == len(region_sizes):
            preapproved_quotas = quota_manager.get_quota_change_options(region_sizes[0])
            smaller_quotas = quota_manager.get_smaller_quota_options(region_sizes[0])

        if self.size in smaller_quotas:
            self.add_note(
                "Quota size '%s' is in list of smaller quotas: %s"
                % (self.size, smaller_quotas)
            )
            return True

        if changed_in_period:
            self.add_note(
                "Quota has already been updated within the auto " "approve time limit."
            )
            return False

        if self.size not in preapproved_quotas:
            self.add_note(
                "Quota size '%s' not in preapproved list: %s"
                % (self.size, preapproved_quotas)
            )
            return False

        self.add_note(
            "Quota size '%s' in preapproved list: %s" % (self.size, preapproved_quotas)
        )
        return True

    def _validate(self):
        # Make sure the project id is valid and can be used
        self.action.valid = validate_steps(
            [
                self._validate_project_id,
                self._validate_quota_size_exists,
                self._validate_regions_exist,
                self._validate_usage_lower_than_quota,
            ]
        )
        self.action.save()

    def _prepare(self):
        self._validate()
        # Set auto-approval
        self.set_auto_approve(self._can_auto_approve())

    def _approve(self):
        self._validate()

        if not self.valid or self.action.state == "completed":
            return

        # Use manager here instead, it will make it easier to add has_more
        # in later
        for region in self.regions:
            self._set_region_quota(region, self.size)

        self.action.state = "completed"
        self.action.task.cache["project_id"] = self.project_id
        self.action.task.cache["size"] = self.size

        self.action.save()

    def _submit(self, token_data, keystone_user=None):
        """
        Nothing to do here. Everything is done at approve.
        """
        pass
Exemplo n.º 7
0
def make_task_config(task_class):

    config_group = groups.DynamicNameConfigGroup()
    config_group.register_child_config(
        fields.BoolConfig(
            "allow_auto_approve",
            help_text="Override if this task allows auto_approval. "
            "Otherwise uses task default.",
            default=task_class.allow_auto_approve,
        ))
    config_group.register_child_config(
        fields.ListConfig(
            "additional_actions",
            help_text="Additional actions to be run as part of the task "
            "after default actions.",
            default=task_class.additional_actions or [],
        ))
    config_group.register_child_config(
        fields.IntConfig(
            "token_expiry",
            help_text="Override for the task token expiry. "
            "Otherwise uses task default.",
            default=task_class.token_expiry,
        ))
    config_group.register_child_config(
        fields.DictConfig(
            "actions",
            help_text="Action config overrides over the action defaults. "
            "See 'adjutant.workflow.action_defaults'.",
            is_json=True,
            default=task_class.action_config or {},
            sample_default={
                "SomeCustomAction": {
                    "some_action_setting": "<a-uuid-probably>"
                }
            },
        ))
    config_group.register_child_config(
        fields.DictConfig(
            "emails",
            help_text="Email config overrides for this task over task defaults."
            "See 'adjutant.workflow.emails'.",
            is_json=True,
            default=task_class.email_config or {},
            sample_default={
                "initial": None,
                "token": {
                    "subject": "Some custom subject",
                },
            },
        ))
    config_group.register_child_config(
        fields.DictConfig(
            "notifications",
            help_text=
            "Notification config overrides for this task over task defaults."
            "See 'adjutant.workflow.notifications'.",
            is_json=True,
            default=task_class.notification_config or {},
            sample_default={
                "standard_handlers": ["EmailNotification"],
                "error_handlers": ["EmailNotification"],
                "standard_handler_config": {
                    "EmailNotification": {
                        "emails": ["*****@*****.**"],
                        "reply": "*****@*****.**",
                    }
                },
                "error_handler_config": {
                    "EmailNotification": {
                        "emails": ["*****@*****.**"],
                        "reply": "*****@*****.**",
                    }
                },
            },
        ))
    return config_group
Exemplo n.º 8
0
class MailingListSubscribeAction(base.MocBaseAction):

    required = ['email']

    serializer = serializers.MailingListSubscribeSerializer

    config_group = conf_group.DynamicNameConfigGroup(children=[
        conf_field.StrConfig(
            "private_key",
            help_text="Location of private key for mailing list server.",
            default="/.ssh/id_rsa",
            sample_default="/.ssh/id_rsa",
        ),
        conf_field.HostNameConfig(
            "host",
            help_text="Mailing list server host.",
            default="mail.massopen.cloud",
            sample_default="mail.massopen.cloud",
        ),
        conf_field.IntConfig(
            "port",
            help_text="Mailing list server SSH port",
            default=22,
            sample_default=22,
        ),
        conf_field.StrConfig(
            "user",
            help_text="Mailing list server user.",
            default="moc-tools",
            sample_default="moc-tools",
        ),
        conf_field.StrConfig(
            "list",
            help_text="Mailing list to add users to.",
            default="kaizen-users",
            sample_default="kaizen-users",
        ),
    ], )

    def _get_email(self):
        if CONF.identity.username_is_email:
            return self.action.task.keystone_user['username']

    def _approve(self):
        try:
            with Mailman(self.config.host, self.config.port, self.config.user,
                         self.config.private_key) as mailman:
                if mailman.is_already_subscribed(self._get_email(),
                                                 self.config.list):
                    self.add_note('%s already subscribed to mailing list.' %
                                  self._get_email())
                else:
                    mailman.subscribe(self._get_email(), self.config.list)
                    self.add_note(
                        '%s successfully subscribed to mailing list.' %
                        self._get_email())
        except paramiko.ssh_exception.SSHException as e:
            self.add_note('Unable to connect to Mailing List server. '
                          'Proceeding regardless. %s' % str(e))

        self.action.state = 'complete'
        self.action.save()

    def _submit(self, token_data):
        pass
Exemplo n.º 9
0
class EmailNotification(base.BaseNotificationHandler):
    """
    Basic email notification handler. Will
    send an email with the given templates.
    """

    config_group = groups.DynamicNameConfigGroup(children=[
        fields.ListConfig(
            "emails",
            help_text="List of email addresses to send this notification to.",
            item_type=types.String(regex=constants.EMAIL_REGEX),
            default=[],
        ),
        fields.StrConfig(
            "from",
            help_text="From email for this notification.",
            regex=constants.EMAIL_WITH_TEMPLATE_REGEX,
            sample_default="bounce+%(task_uuid)[email protected]",
        ),
        fields.StrConfig(
            "reply",
            help_text="Reply-to email for this notification.",
            regex=constants.EMAIL_REGEX,
            sample_default="*****@*****.**",
        ),
        fields.StrConfig(
            "template",
            help_text="Email template for this notification. "
            "No template will cause the email not to send.",
            default="notification.txt",
        ),
        fields.StrConfig(
            "html_template",
            help_text="Email html template for this notification.",
        ),
    ])

    def _notify(self, task, notification):
        conf = self.config(task, notification)
        if not conf or not conf["emails"]:
            # Log that we did this!!
            note = ("Skipped sending notification for task: %s (%s) "
                    "as notification handler conf is None, or no emails "
                    "were configured." % (task.task_type, task.uuid))
            self.logger.info("(%s) - %s" % (timezone.now(), note))
            return

        template = loader.get_template(conf["template"],
                                       using="include_etc_templates")
        html_template = conf["html_template"]
        if html_template:
            html_template = loader.get_template(html_template,
                                                using="include_etc_templates")

        context = {"task": task, "notification": notification}

        if CONF.workflow.horizon_url:
            task_url = CONF.workflow.horizon_url
            notification_url = CONF.workflow.horizon_url
            if not task_url.endswith("/"):
                task_url += "/"
            if not notification_url.endswith("/"):
                notification_url += "/"
            task_url += "management/tasks/%s" % task.uuid
            notification_url += "management/notifications/%s" % notification.uuid
            context["task_url"] = task_url
            context["notification_url"] = notification_url

        if notification.error:
            subject = "Error - %s notification" % task.task_type
        else:
            subject = "%s notification" % task.task_type
        try:
            message = template.render(context)

            # from_email is the return-path and is distinct from the
            # message headers
            from_email = conf["from"]
            if not from_email:
                from_email = conf["reply"]
            elif "%(task_uuid)s" in from_email:
                from_email = from_email % {"task_uuid": task.uuid}

            # these are the message headers which will be visible to
            # the email client.
            headers = {
                "X-Adjutant-Task-UUID": task.uuid,
                # From needs to be set to be disctinct from return-path
                "From": conf["reply"],
                "Reply-To": conf["reply"],
            }

            email = EmailMultiAlternatives(subject,
                                           message,
                                           from_email,
                                           conf["emails"],
                                           headers=headers)

            if html_template:
                email.attach_alternative(html_template.render(context),
                                         "text/html")

            email.send(fail_silently=False)
            notification.acknowledged = True
            notification.save()
        except SMTPException as e:
            notes = {
                "errors":
                [("Error: '%s' while sending email notification") % e]
            }
            error_notification = Notification.objects.create(
                task=notification.task, notes=notes, error=True)
            error_notification.save()
Exemplo n.º 10
0
class SendAdditionalEmailAction(BaseAction):

    serializer = serializers.SendAdditionalEmailSerializer

    config_group = groups.DynamicNameConfigGroup(children=[
        _build_default_email_group("prepare"),
        _build_default_email_group("approve"),
        _build_default_email_group("submit"),
    ], )

    def set_email(self, conf):
        self.emails = set()
        if conf.get("email_current_user"):
            self.add_note("Adding the current user's email address")
            if CONF.identity.username_is_email:
                self.emails.add(self.action.task.keystone_user["username"])
            else:
                try:
                    id_manager = user_store.IdentityManager()
                    email = id_manager.get_user(
                        self.action.task.keystone_user["user_id"]).email
                    self.emails.add(email)
                except AttributeError:
                    self.add_note("Could not add current user email address")

        if conf.get("email_roles"):
            roles = set(conf.get("email_roles"))
            project_id = self.action.task.keystone_user["project_id"]
            self.add_note("Adding email addresses for roles %s in project %s" %
                          (roles, project_id))

            id_manager = user_store.IdentityManager()
            users = id_manager.list_users(project_id)
            for user in users:
                user_roles = [role.name for role in user.roles]
                if roles.intersection(user_roles):
                    if CONF.identity.username_is_email:
                        self.emails.add(user.name)
                    else:
                        self.emails.add(user.email)

        if conf.get("email_task_cache"):
            task_emails = self.action.task.cache.get("additional_emails", [])
            if isinstance(task_emails, six.string_types):
                task_emails = [task_emails]
            for email in task_emails:
                self.emails.add(email)

        for email in conf.get("email_additional_addresses"):
            self.emails.add(email)

    def _validate(self):
        self.action.valid = True
        self.action.save()

    def _prepare(self):
        self.perform_action("prepare")

    def _approve(self):
        self.perform_action("approve")

    def _submit(self, token_data, keystone_user=None):
        self.perform_action("submit")

    def perform_action(self, stage):
        self._validate()

        task = self.action.task
        for action in task.actions:
            if not action.valid:
                return

        email_conf = self.config.get(stage)

        # If either of these are false we won't be sending anything.
        if not email_conf or not email_conf.get("template"):
            return

        self.set_email(email_conf)

        if not self.emails:
            self.add_note(self.emails)
            self.add_note("Email address not set. Stage: %s" % stage)
            return

        self.add_note("Sending emails to: %s" % self.emails)

        actions = {}
        for action in task.actions:
            act = action.get_action()
            actions[str(act)] = act

        context = {"task": task, "actions": actions}

        result = send_email(self.emails, context, email_conf, task)

        if not result:
            self.add_note("Unable to send additional email. Stage: %s" % stage)
        else:
            self.add_note("Additional email sent. Stage: %s" % stage)
Exemplo n.º 11
0
class UserList(tasks.InviteUser):

    url = r"^openstack/users/?$"

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.ListConfig(
                "blacklisted_roles",
                help_text="Users with any of these roles will be hidden from the user list.",
                default=[],
                sample_default=["admin"],
            ),
        ]
    )

    @utils.mod_or_admin
    def get(self, request):
        """Get a list of all users who have been added to a project"""
        class_conf = self.config
        blacklisted_roles = class_conf.blacklisted_roles

        user_list = []
        id_manager = user_store.IdentityManager()
        project_id = request.keystone_user["project_id"]
        project = id_manager.get_project(project_id)

        can_manage_roles = id_manager.get_manageable_roles(
            request.keystone_user["roles"]
        )

        active_emails = set()
        for user in id_manager.list_users(project):
            skip = False
            roles = []
            for role in user.roles:
                if role.name in blacklisted_roles:
                    skip = True
                    continue
                roles.append(role.name)
            if skip:
                continue
            inherited_roles = []
            for role in user.inherited_roles:
                if role.name in blacklisted_roles:
                    skip = True
                    continue
                inherited_roles.append(role.name)
            if skip:
                continue

            email = getattr(user, "email", "")
            enabled = user.enabled
            user_status = "Active" if enabled else "Account Disabled"
            active_emails.add(email)
            user_list.append(
                {
                    "id": user.id,
                    "name": user.name,
                    "email": email,
                    "roles": roles,
                    "inherited_roles": inherited_roles,
                    "cohort": "Member",
                    "status": user_status,
                    "manageable": set(can_manage_roles).issuperset(roles),
                }
            )

        for user in id_manager.list_inherited_users(project):
            skip = False
            roles = []
            for role in user.roles:
                if role.name in blacklisted_roles:
                    skip = True
                    continue
                roles.append(role.name)
            if skip:
                continue

            email = getattr(user, "email", "")
            enabled = user.enabled
            user_status = "Active" if enabled else "Account Disabled"
            user_list.append(
                {
                    "id": user.id,
                    "name": user.name,
                    "email": email,
                    "roles": roles,
                    "inherited_roles": [],
                    "cohort": "Inherited",
                    "status": user_status,
                    "manageable": False,
                }
            )

        # Get my active tasks for this project:
        project_tasks = models.Task.objects.filter(
            project_id=project_id,
            task_type="invite_user_to_project",
            completed=0,
            cancelled=0,
        )

        registrations = []
        for task in project_tasks:
            status = "Invited"
            for token in task.tokens:
                if token.expired:
                    status = "Expired"

            for notification in task.notifications:
                if notification.error:
                    status = "Failed"

            for action in task.actions:
                if not action.valid:
                    status = "Invalid"

            task_data = {}
            for action in task.actions:
                task_data.update(action.action_data)

            registrations.append(
                {"uuid": task.uuid, "task_data": task_data, "status": status}
            )

        for task in registrations:
            # NOTE(adriant): commenting out for now as it causes more confusion
            # than it helps. May uncomment once different duplication checking
            # measures are in place.
            # if task['task_data']['email'] not in active_emails:
            user = {
                "id": task["uuid"],
                "name": task["task_data"]["email"],
                "email": task["task_data"]["email"],
                "roles": task["task_data"]["roles"],
                "inherited_roles": task["task_data"]["inherited_roles"],
                "cohort": "Invited",
                "status": task["status"],
            }
            if not CONF.identity.username_is_email:
                user["name"] = task["task_data"]["username"]

            user_list.append(user)

        return Response({"users": user_list})
Exemplo n.º 12
0
class UserRoles(BaseDelegateAPI):

    url = r"^openstack/users/(?P<user_id>\w+)/roles/?$"

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.ListConfig(
                "blacklisted_roles",
                help_text="User with these roles will return not found.",
                default=[],
                sample_default=["admin"],
            ),
        ]
    )

    task_type = "edit_user_roles"

    @utils.mod_or_admin
    def get(self, request, user_id):
        """ Get role info based on the user id. """
        id_manager = user_store.IdentityManager()
        user = id_manager.get_user(user_id)

        no_user = {"errors": ["No user with this id."]}
        if not user:
            return Response(no_user, status=404)

        project_id = request.keystone_user["project_id"]
        project = id_manager.get_project(project_id)

        class_conf = self.config
        blacklisted_roles = class_conf.blacklisted_roles

        roles = [role.name for role in id_manager.get_roles(user, project)]
        roles_blacklisted = set(blacklisted_roles) & set(roles)
        inherited_roles = [
            role.name for role in id_manager.get_roles(user, project, True)
        ]
        inherited_roles_blacklisted = set(blacklisted_roles) & set(inherited_roles)

        if not roles or roles_blacklisted or inherited_roles_blacklisted:
            return Response(no_user, status=404)
        return Response({"roles": roles, "inherited_roles": inherited_roles})

    @utils.mod_or_admin
    def put(self, args, **kwargs):
        """ Add user roles to the current project. """
        kwargs["remove_role"] = False
        return self._edit_user(args, **kwargs)

    @utils.mod_or_admin
    def delete(self, args, **kwargs):
        """Revoke user roles to the current project.

        This only supports Active users
        """
        kwargs["remove_role"] = True
        return self._edit_user(args, **kwargs)

    def _edit_user(self, request, user_id, remove_role=False, format=None):
        """ Helper function to add or remove roles from a user """
        request.data["remove"] = remove_role
        if "project_id" not in request.data:
            request.data["project_id"] = request.keystone_user["project_id"]
        request.data["user_id"] = user_id

        self.logger.info(
            "(%s) - New EditUser %s request." % (timezone.now(), request.method)
        )

        self.task_manager.create_from_request(self.task_type, request)

        return Response({"notes": ["task created"]}, status=202)
Exemplo n.º 13
0
class UserDetail(BaseDelegateAPI):

    url = r"^openstack/users/(?P<user_id>\w+)/?$"

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.ListConfig(
                "blacklisted_roles",
                help_text="User with these roles will return not found.",
                default=[],
                sample_default=["admin"],
            ),
        ]
    )

    @utils.mod_or_admin
    def get(self, request, user_id):
        """
        Get user info based on the user id.

        Will only find users in your project.
        """
        id_manager = user_store.IdentityManager()
        user = id_manager.get_user(user_id)

        no_user = {"errors": ["No user with this id."]}
        if not user:
            return Response(no_user, status=404)

        class_conf = self.config
        blacklisted_roles = class_conf.blacklisted_roles

        project_id = request.keystone_user["project_id"]
        project = id_manager.get_project(project_id)

        roles = [role.name for role in id_manager.get_roles(user, project)]
        roles_blacklisted = set(blacklisted_roles) & set(roles)
        inherited_roles = [
            role.name for role in id_manager.get_roles(user, project, True)
        ]
        inherited_roles_blacklisted = set(blacklisted_roles) & set(inherited_roles)

        if not roles or roles_blacklisted or inherited_roles_blacklisted:
            return Response(no_user, status=404)
        return Response(
            {
                "id": user.id,
                "username": user.name,
                "email": getattr(user, "email", ""),
                "roles": roles,
                "inherited_roles": inherited_roles,
            }
        )

    @utils.mod_or_admin
    def delete(self, request, user_id):
        """
        Remove this user from the project.
        This may cancel a pending user invite, or simply revoke roles.
        """
        id_manager = user_store.IdentityManager()
        user = id_manager.get_user(user_id)
        project_id = request.keystone_user["project_id"]
        # NOTE(dale): For now, we only support cancelling pending invites.
        if user:
            return Response(
                {
                    "errors": [
                        "Revoking keystone users not implemented. "
                        "Try removing all roles instead."
                    ]
                },
                status=501,
            )
        project_tasks = models.Task.objects.filter(
            project_id=project_id,
            task_type="invite_user_to_project",
            completed=0,
            cancelled=0,
        )
        for task in project_tasks:
            if task.uuid == user_id:
                self.task_manager.cancel(task)
                return Response("Cancelled pending invite task!", status=200)
        return Response("Not found.", status=404)
Exemplo n.º 14
0
class ResetUserPasswordAction(UserNameAction, UserMixin):
    """
    Simple action to reset a password for a given user.
    """

    required = ["domain_name", "username", "email"]

    serializer = serializers.ResetUserPasswordSerializer

    config_group = groups.DynamicNameConfigGroup(
        children=[
            fields.ListConfig(
                "blacklisted_roles",
                help_text="Users with these roles cannot reset their passwords.",
                default=[],
                sample_default=["admin"],
            ),
        ],
    )

    def __init__(self, *args, **kwargs):
        super(ResetUserPasswordAction, self).__init__(*args, **kwargs)

    def _validate_user_roles(self):
        id_manager = user_store.IdentityManager()

        roles = id_manager.get_all_roles(self.user)

        user_roles = []
        for roles in roles.values():
            user_roles.extend(role.name for role in roles)

        if set(self.config.blacklisted_roles) & set(user_roles):
            self.add_note("Cannot reset users with blacklisted roles.")
            return False

        return True

    def _validate_user_email(self):
        # NOTE(adriant): We only need to check the USERNAME_IS_EMAIL=False
        # case since '_validate_username_exists' will ensure the True case
        if not CONF.identity.username_is_email:
            if self.user and (
                getattr(self.user, "email", None).lower() != self.email.lower()
            ):
                self.add_note("Existing user with non-matching email.")
                return False

        self.action.need_token = True
        self.set_token_fields(["password"])
        self.add_note("Existing user with matching email.")
        return True

    def _validate(self):
        # Here, the order of validation matters
        # as each one adds new class variables
        self.action.valid = validate_steps(
            [
                self._validate_domain_name,
                self._validate_username_exists,
                self._validate_user_roles,
                self._validate_user_email,
            ]
        )
        self.action.save()

    def _prepare(self):
        self._validate()
        self.set_auto_approve()

    def _approve(self):
        self._validate()

    def _submit(self, token_data, keystone_user=None):
        self._validate()

        if not self.valid:
            return

        self.update_password(token_data["password"])
        self.add_note("User %s password has been changed." % self.username)
Exemplo n.º 15
0
class MocNewProjectAction(base.MocBaseAction):
    """Creates a new project for the current authenticated user."""

    required = [
        'project_name',
        'description',
        'domain_id',

        # TODO(knikolla): It should be possible to fetch these from
        # SSO once we support OAuth 2.0 access tokens.
        'organization',
        'organization_role',
        'services',
        'phone',
        'moc_contact',
    ]

    serializer = serializers.MocNewProjectSerializer

    config_group = config_groups.DynamicNameConfigGroup(children=[
        config_fields.ListConfig(
            "default_roles",
            help_text="Roles to be given on project to the creating user.",
            default=["member", "project_admin"],
            sample_default=["member", "project_admin"]),
        config_fields.ListConfig(
            "enabled_services",
            help_text="Other services configured in the cloud.",
            default=['openshift'],
        ),
    ], )

    def _validate_project_name(self):
        # Make sure project id doesn't exist.
        project = self.identity.find_project(self.project_name, self.domain_id)
        if project:
            return False
        return True

    def _validate_services(self):
        for service in self.services:
            if service not in self.config.enabled_services:
                return False
        return True

    def _get_email(self):
        return self.action.task.keystone_user['username']

    def write_to_approve_journal(self):
        user_ref = self._get_user()
        project_ref = {
            'name': self.project_name,
            'domain_id': self.domain_id,
            'description': self.description,
            'organization': self.organization,
            'owner': self._get_email(),
        }
        self.approve_journal.append(
            operations.CreateProjectOperation(self.services, project_ref))
        self.approve_journal.append(
            operations.AddUserToProjectOperation(self.services, user_ref,
                                                 project_ref,
                                                 self.config.default_roles))

    def write_to_submit_journal(self, token_data):
        pass