Пример #1
0
def grantable_permissions(session, standard_graph):
    perm_grant, _ = Permission.get_or_create(session, name=PERMISSION_GRANT, description="")
    perm0, _ = Permission.get_or_create(session, name="grantable", description="")
    perm1, _ = Permission.get_or_create(session, name="grantable.one", description="")
    perm2, _ = Permission.get_or_create(session, name="grantable.two", description="")
    session.commit()

    return perm_grant, perm0, perm1, perm2
Пример #2
0
    def post(self):
        can_create = self.current_user.my_creatable_permissions()
        if not can_create:
            return self.forbidden()

        form = PermissionCreateForm(self.request.arguments)
        if not form.validate():
            return self.render(
                "permission-create.html", form=form,
                alerts=self.get_form_alerts(form.errors)
            )

        # A user is allowed to create a permission if the name matches any of the globs that they
        # are given access to via PERMISSION_CREATE, as long as the permission does not match a
        # reserved name. (Unless specifically granted.)
        allowed = False
        for creatable in can_create:
            if matches_glob(creatable, form.data["name"]):
                allowed = True

        for failure_message in test_reserved_names(form.data["name"]):
            form.name.errors.append(failure_message)

        if not allowed:
            form.name.errors.append(
                "Permission name does not match any of your allowed patterns."
            )

        if form.name.errors:
            return self.render(
                "permission-create.html", form=form,
                alerts=self.get_form_alerts(form.errors),
            )

        permission = Permission(name=form.data["name"], description=form.data["description"])
        try:
            permission.add(self.session)
            self.session.flush()
        except IntegrityError:
            self.session.rollback()
            form.name.errors.append(
                "Name already in use. Permissions must be unique."
            )
            return self.render(
                "permission-create.html", form=form, can_create=can_create,
                alerts=self.get_form_alerts(form.errors),
            )

        self.session.commit()

        AuditLog.log(self.session, self.current_user.id, 'create_permission',
                     'Created permission.', on_permission_id=permission.id)

        # No explicit refresh because handler queries SQL.
        return self.redirect("/permissions/{}".format(permission.name))
Пример #3
0
def test_permission_grant_to_owners(session, standard_graph, groups, grantable_permissions):
    """Test we're getting correct owners according to granted
    'grouper.permission.grant' permissions."""
    perm_grant, _, perm1, perm2 = grantable_permissions

    assert not get_owners_by_grantable_permission(session), 'nothing to begin with'

    # grant a grant on a non-existent permission
    grant_permission(groups["auditors"], perm_grant, argument="notgrantable.one")
    assert not get_owners_by_grantable_permission(session), 'ignore grants for non-existent perms'

    # grant a wildcard grant -- make sure all permissions are represented and
    # the grant isn't inherited
    grant_permission(groups["all-teams"], perm_grant, argument="grantable.*")
    owners_by_arg_by_perm = get_owners_by_grantable_permission(session)
    expected = [groups['all-teams']]
    assert owners_by_arg_by_perm[perm1.name]['*'] == expected, 'grants are not inherited'
    assert len(owners_by_arg_by_perm) == 2
    assert len(owners_by_arg_by_perm[perm1.name]) == 1
    assert len(owners_by_arg_by_perm[perm2.name]) == 1

    # grant on argument substring
    grant_permission(groups["team-sre"], perm_grant, argument="{}/somesubstring*".format(
            perm1.name))
    owners_by_arg_by_perm = get_owners_by_grantable_permission(session)
    expected = [groups['all-teams']]
    assert owners_by_arg_by_perm[perm1.name]['*'] == expected
    expected = [groups["team-sre"]]
    assert owners_by_arg_by_perm[perm1.name]['somesubstring*'] == expected

    # make sure get_owner() respect substrings
    res = [o for o, a in get_owner_arg_list(session, perm1, "somesubstring",
            owners_by_arg_by_perm=owners_by_arg_by_perm)]
    assert (sorted(res) == sorted([groups["all-teams"], groups["team-sre"]]),
            "should include substring wildcard matches")

    res = [o for o, a in get_owner_arg_list(session, perm1, "othersubstring",
            owners_by_arg_by_perm=owners_by_arg_by_perm)]
    assert sorted(res) == [groups["all-teams"]], "negative test of substring wildcard matches"

    # permission admins have all the power
    perm_admin, _ = Permission.get_or_create(session, name=PERMISSION_ADMIN, description="")
    session.commit()
    grant_permission(groups["security-team"], perm_admin)

    owners_by_arg_by_perm = get_owners_by_grantable_permission(session)
    all_permissions = Permission.get_all(session)
    for perm in all_permissions:
        assert perm.name in owners_by_arg_by_perm, 'all permission should be represented'
        assert groups["security-team"] in owners_by_arg_by_perm[perm.name]["*"], \
                'permission admin should be wildcard owners'
Пример #4
0
def filter_grantable_permissions(session, grants, all_permissions=None):
    """For a given set of PERMISSION_GRANT permissions, return all permissions
    that are grantable.

    Args:
        session (sqlalchemy.orm.session.Session); database session
        grants ([Permission, ...]): PERMISSION_GRANT permissions
        all_permissions ({name: Permission}): all permissions to check against

    Returns:
        list of (Permission, argument) that is grantable by list of grants
        sorted by permission name and argument.
    """

    if all_permissions is None:
        all_permissions = {permission.name: permission for permission in
                Permission.get_all(session)}

    result = []
    for grant in grants:
        assert grant.name == PERMISSION_GRANT

        grantable = grant.argument.split('/', 1)
        if not grantable:
            continue
        for name, permission_obj in all_permissions.iteritems():
            if matches_glob(grantable[0], name):
                result.append((permission_obj,
                               grantable[1] if len(grantable) > 1 else '*', ))

    return sorted(result, key=lambda x: x[0].name + x[1])
Пример #5
0
def test_permission_exclude_inactive(session, standard_graph):
    """Ensure disabled groups are excluded from permission data."""
    group = Group.get(session, name="team-sre")
    permission = Permission.get(session, "ssh")
    assert "team-sre" in [g[0] for g in permission.get_mapped_groups()]
    group.disable()
    assert "team-sre" not in [g[0] for g in permission.get_mapped_groups()]
Пример #6
0
def permissions(session):
    permissions = {
        permission: Permission.get_or_create(
            session, name=permission, description="{} permission".format(permission))[0]
        for permission in ("ssh", "sudo")
    }
    session.commit()
    return permissions
Пример #7
0
def permissions(session):
    permissions = {
        permission: Permission.get_or_create(
            session, name=permission, description="{} permission".format(permission))[0]
        for permission in ("ssh", "sudo", "audited", PERMISSION_AUDITOR)
    }
    permissions["audited"].enable_auditing()
    session.commit()
    return permissions
Пример #8
0
def sync_db_command(args):
    db_engine = get_db_engine(get_database_url(settings))
    Model.metadata.create_all(db_engine)

    # Add some basic database structures we know we will need if they don't exist.
    session = make_session()

    for name, description in SYSTEM_PERMISSIONS:
        test = Permission.get(session, name)
        if test:
            continue
        permission = Permission(name=name, description=description)
        try:
            permission.add(session)
            session.flush()
        except IntegrityError:
            session.rollback()
            raise Exception('Failed to create permission: %s' % (name, ))
        session.commit()

    # This group is needed to bootstrap a Grouper installation.
    admin_group = Group.get(session, name="grouper-administrators")
    if not admin_group:
        admin_group = Group(
                groupname="grouper-administrators",
                description="Administrators of the Grouper system.",
                canjoin="nobody",
        )

        try:
            admin_group.add(session)
            session.flush()
        except IntegrityError:
            session.rollback()
            raise Exception('Failed to create group: grouper-administrators')

        for permission_name in (GROUP_ADMIN, PERMISSION_ADMIN, USER_ADMIN):
            permission = Permission.get(session, permission_name)
            assert permission, "Permission should have been created earlier!"
            admin_group.grant_permission(permission)

        session.commit()
Пример #9
0
def permissions(session):
    permissions = {
        permission: Permission.get_or_create(
            session,
            name=permission,
            description="{} permission".format(permission))[0]
        for permission in ("ssh", "sudo", "audited", PERMISSION_AUDITOR)
    }
    permissions["audited"].enable_auditing()
    session.commit()
    return permissions
Пример #10
0
def get_owners_by_grantable_permission(session):
    """
    Returns all known permission arguments with owners. This consolidates
    permission grants supported by grouper itself as well as any grants
    governed by plugins.

    Args:
        session(sqlalchemy.orm.session.Session): database session

    Returns:
        A map of permission to argument to owners of the form {permission:
        {argument: [owner1, ...], }, } where 'owners' are models.Group objects.
        And 'argument' can be '*' which means 'anything'.
    """
    all_permissions = {permission.name: permission for permission in Permission.get_all(session)}
    all_groups = session.query(Group).filter(Group.enabled == True).all()

    owners_by_arg_by_perm = defaultdict(lambda: defaultdict(list))
    for group in all_groups:
        group_permissions = session.query(
                Permission.name,
                PermissionMap.argument,
                PermissionMap.granted_on,
                Group,
        ).filter(
                PermissionMap.group_id == Group.id,
                Group.id == group.id,
                Permission.id == PermissionMap.permission_id,
        ).all()

        # special case permission admins
        if any(filter(lambda g: g.name == PERMISSION_ADMIN, group_permissions)):
            for perm_name in all_permissions:
                owners_by_arg_by_perm[perm_name]["*"].append(group)
            continue

        grants = [gp for gp in group_permissions if gp.name == PERMISSION_GRANT]

        for perm, arg in filter_grantable_permissions(session, grants,
                all_permissions=all_permissions):
            owners_by_arg_by_perm[perm.name][arg].append(group)

    # merge in plugin results
    for plugin in get_plugins():
        res = plugin.get_owner_by_arg_by_perm(session) or {}
        for perm, owners_by_arg in res.items():
            for arg, owners in owners_by_arg.items():
                owners_by_arg_by_perm[perm][arg] += owners

    return owners_by_arg_by_perm
Пример #11
0
    def get(self, name=None):
        # TODO: use cached data instead, add refresh to appropriate redirects.
        permission = Permission.get(self.session, name)
        if not permission:
            return self.notfound()

        can_delete = self.current_user.permission_admin
        mapped_groups = permission.get_mapped_groups()
        log_entries = permission.my_log_entries()

        self.render(
            "permission.html", permission=permission, can_delete=can_delete,
            mapped_groups=mapped_groups, log_entries=log_entries,
        )
    def post(self, name=None):
        if not self.current_user.permission_admin:
            return self.forbidden()

        permission = Permission.get(self.session, name)
        if not permission:
            return self.notfound()

        permission.enable_auditing()
        self.session.commit()

        AuditLog.log(self.session, self.current_user.id, 'enable_auditing',
                     'Enabled auditing.', on_permission_id=permission.id)

        # No explicit refresh because handler queries SQL.
        return self.redirect("/permissions/{}".format(permission.name))
Пример #13
0
    def post(self, name=None):
        grantable = self.current_user.my_grantable_permissions()
        if not grantable:
            return self.forbidden()

        group = Group.get(self.session, None, name)
        if not group:
            return self.notfound()

        form = PermissionGrantForm(self.request.arguments)
        form.permission.choices = [["", "(select one)"]]
        for perm in grantable:
            grantable_str = "{} ({})".format(perm[0].name, perm[1])
            form.permission.choices.append([perm[0].name, grantable_str])

        if not form.validate():
            return self.render(
                "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)
            )

        permission = Permission.get(self.session, form.data["permission"])
        if not permission:
            return self.notfound()  # Shouldn't happen.

        allowed = False
        for perm in grantable:
            if perm[0].name == permission.name:
                if matches_glob(perm[1], form.data["argument"]):
                    allowed = True
        if not allowed:
            form.argument.errors.append("You do not have grant authority over that permission/argument combination.")
            return self.render(
                "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)
            )

        # If the permission is audited, then see if the subtree meets auditing requirements.
        if permission.audited:
            fail_message = (
                "Permission is audited and this group (or a subgroup) contains "
                + "owners, np-owners, or managers who have not received audit training."
            )
            try:
                permission_ok = assert_controllers_are_auditors(group)
            except UserNotAuditor as e:
                permission_ok = False
                fail_message = e
            if not permission_ok:
                form.permission.errors.append(fail_message)
                return self.render(
                    "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)
                )

        try:
            group.grant_permission(permission, argument=form.data["argument"])
        except IntegrityError:
            form.argument.errors.append("Permission and Argument already mapped to this group.")
            return self.render(
                "permission-grant.html", form=form, group=group, alerts=self.get_form_alerts(form.errors)
            )

        self.session.commit()

        AuditLog.log(
            self.session,
            self.current_user.id,
            "grant_permission",
            "Granted permission with argument: {}".format(form.data["argument"]),
            on_permission_id=permission.id,
            on_group_id=group.id,
        )

        return self.redirect("/groups/{}?refresh=yes".format(group.name))
Пример #14
0
    def post(self, group_id=None, name=None):
        group = Group.get(self.session, group_id, name)
        if not group:
            return self.notfound()

        # only owner of group can request permissions for that group
        role_index = self.current_user.my_role_index(group.my_members())
        if role_index not in OWNER_ROLE_INDICES:
            return self.forbidden()

        # check inputs
        args_by_perm = get_grantable_permissions(self.session,
                settings.restricted_ownership_permissions)
        dropdown_form, text_form = GroupPermissionRequest._get_forms(args_by_perm,
                self.request.arguments)

        argument_type = self.request.arguments.get("argument_type")
        if argument_type and argument_type[0] == "text":
            form = text_form
        elif argument_type and argument_type[0] == "dropdown":
            form = dropdown_form
            form.argument.choices = [(a, a) for a in args_by_perm[form.permission_name.data]]
        else:
            # someone messing with the form
            self.log_message("unknown argument type", group_name=group.name,
                    argument_type=argument_type)
            return self.forbidden()

        if not form.validate():
            return self.render(
                    "group-permission-request.html", dropdown_form=dropdown_form,
                    text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm),
                    alerts=self.get_form_alerts(form.errors),
                    dropdown_help=settings.permission_request_dropdown_help,
                    text_help=settings.permission_request_text_help,
                    )

        permission = Permission.get(self.session, form.permission_name.data)
        assert permission is not None, "our prefilled permission should exist or we have problems"

        # save off request
        try:
            permissions.create_request(self.session, self.current_user, group,
                    permission, form.argument.data, form.reason.data)
        except permissions.RequestAlreadyGranted:
            alerts = [Alert("danger", "This group already has this permission and argument.")]
        except permissions.RequestAlreadyExists:
            alerts = [Alert("danger",
                    "Request for permission and argument already exists, please wait patiently.")]
        except permissions.NoOwnersAvailable:
            self.log_message("prefilled perm+arg have no owner", group_name=group.name,
                    permission_name=permission.name, argument=form.argument.data)
            alerts = [Alert("danger", "No owners available for requested permission and argument."
                    " If this error persists please contact an adminstrator.")]
        else:
            alerts = None

        if alerts:
            return self.render(
                    "group-permission-request.html", dropdown_form=dropdown_form,
                    text_form=text_form, group=group, args_by_perm_json=json.dumps(args_by_perm),
                    alerts=alerts,
                    )
        else:
            return self.redirect("/groups/{}".format(group.name))
Пример #15
0
def user_admin_perm_to_auditors(session, groups):
    """Adds a USER_ADMIN permission to the "auditors" group"""
    user_admin_perm, is_new = Permission.get_or_create(session, name=USER_ADMIN, description="")
    session.commit()

    grant_permission(groups["auditors"], user_admin_perm)