Skip to content

Commit

Permalink
Merge pull request #202 from RedHatInsights/master
Browse files Browse the repository at this point in the history
Promote Changes to QA - 2/11/2020
  • Loading branch information
coderbydesign authored Feb 12, 2020
2 parents 34c049a + f64985f commit 5e79962
Show file tree
Hide file tree
Showing 16 changed files with 416 additions and 186 deletions.
3 changes: 2 additions & 1 deletion Jenkinsfile-smoke.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ if (env.CHANGE_ID) {
ocDeployerServiceSets: "rbac",
iqePlugins: ["iqe-rbac-plugin"],
pytestMarker: "rbac_smoke",
configFileCredentialsId: "settings_rbac_smoke"
)
}
}
170 changes: 167 additions & 3 deletions docs/source/specs/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,89 @@
}
}
},
"/roles/{uuid}/access/": {
"get": {
"tags": [
"Role"
],
"summary": "Get access for a role in the tenant",
"operationId": "getRoleAccess",
"parameters": [
{
"name": "uuid",
"in": "path",
"description": "ID of the role",
"required": true,
"schema": {
"type": "string",
"format": "uuid"
}
},
{
"$ref": "#/components/parameters/QueryLimit"
},
{
"$ref": "#/components/parameters/QueryOffset"
}
],
"responses": {
"200": {
"description": "A paginated list of the access objects for a role",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/AccessPagination"
}
}
}
}
}
}
},
"401": {
"description": "Unauthorized"
},
"403": {
"description": "Insufficient permissions to get access for role",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error403"
}
}
}
},
"404": {
"description": "Not Found",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
},
"500": {
"description": "Unexpected Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/policies/": {
"post": {
"tags": [
Expand Down Expand Up @@ -1417,19 +1500,34 @@
}
},
{
"$ref": "#/components/parameters/QueryLimit"
"$ref": "#/components/parameters/QueryLimitNoDefault"
},
{
"$ref": "#/components/parameters/QueryOffset"
}
],
"responses": {
"200": {
"description": "A paginated list of access objects",
"description": "A list of access objects. By default, this request will not be paginated. To received a paginated response, include the `/?limit=` query parameter.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/AccessPagination"
"oneOf": [
{
"$ref": "#/components/schemas/AccessNoPagination"
},
{
"$ref": "#/components/schemas/AccessPagination"
}
]
},
"examples": {
"AccessNoPagination": {
"$ref": "#/components/examples/AccessNoPagination"
},
"AccessPagination": {
"$ref": "#/components/examples/AccessPagination"
}
}
}
}
Expand Down Expand Up @@ -1491,6 +1589,17 @@
"maximum": 1000
}
},
"QueryLimitNoDefault": {
"in": "query",
"name": "limit",
"required": false,
"description": "Parameter for selecting the amount of data returned.",
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 1000
}
},
"NameFilter": {
"in": "query",
"name": "name",
Expand Down Expand Up @@ -2057,6 +2166,10 @@
"type": "integer",
"minimum": 0
},
"accessCount": {
"type": "integer",
"minimum": 0
},
"applications": {
"type": "array",
"items": {
Expand Down Expand Up @@ -2214,6 +2327,24 @@
}
]
},
"AccessNoPagination": {
"allOf": [
{
"type": "object",
"required": [
"data"
],
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Access"
}
}
}
}
]
},
"Status": {
"required": [
"api_version"
Expand Down Expand Up @@ -2265,6 +2396,39 @@
}
}
}
},
"examples": {
"AccessNoPagination": {
"value": {
"data": [
{
"permission": "app-name:*:*",
"resourceDefinitions": []
}
]
}
},
"AccessPagination": {
"value": {
"meta": {
"count": 1,
"limit": 10,
"offset": 0
},
"links": {
"first": "/access/?application=app-name&limit=10",
"next": null,
"previous": null,
"last": "/access/?application=app-name&limit=10"
},
"data": [
{
"permission": "app-name:*:*",
"resourceDefinitions": []
}
]
}
}
}
}
}
9 changes: 5 additions & 4 deletions rbac/management/access/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
"""View for principal access."""
from management.querysets import get_access_queryset
from management.role.serializer import AccessSerializer
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.settings import api_settings
Expand Down Expand Up @@ -83,17 +82,19 @@ def get_queryset(self):

def get(self, request):
"""Provide access data for prinicpal."""
page = self.paginate_queryset(self.get_queryset())
queryset = self.get_queryset()
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data)
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR)

return Response({'data': self.serializer_class(queryset, many=True).data})

@property
def paginator(self):
"""Return the paginator instance associated with the view, or `None`."""
if not hasattr(self, '_paginator'):
if self.pagination_class is None:
if self.pagination_class is None or 'limit' not in self.request.query_params:
self._paginator = None
else:
self._paginator = self.pagination_class()
Expand Down
8 changes: 6 additions & 2 deletions rbac/management/group/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ def add_principals(self, group, principals, account):
"""Process list of principals and add them to the group."""
users = [principal.get('username') for principal in principals]
resp = self.proxy.request_filtered_principals(users, account, limit=len(users))
if 'errors' in resp:
return resp
for item in resp.get('data', []):
username = item['username']
try:
Expand Down Expand Up @@ -354,8 +356,10 @@ def principals(self, request, uuid=None):
serializer = GroupPrincipalInputSerializer(data=request.data)
if serializer.is_valid(raise_exception=True):
principals = serializer.data.pop('principals')
group = self.add_principals(group, principals, account)
output = GroupSerializer(group)
resp = self.add_principals(group, principals, account)
if isinstance(resp, dict) and 'errors' in resp:
return Response(status=resp['status_code'], data=resp['errors'])
output = GroupSerializer(resp)
return Response(status=status.HTTP_200_OK, data=output.data)
else:
if USERNAMES_KEY not in request.query_params:
Expand Down
36 changes: 36 additions & 0 deletions rbac/management/migrations/0012_remove_RonR_resources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Generated by Django 2.2.4 on 2019-11-26 17:03

from django.db import migrations, models


def remove_rbac_roles(apps, schema_editor):
# get all permissions/access objects for RBAC
Access = apps.get_model('management', 'Access')
rbac_permissions = Access.objects.filter(permission__contains='rbac:')

# iterate through all RBAC permissions
for rbac_permission in rbac_permissions:
# get the role for the permission (bubbling up)
role = rbac_permission.role

# check to see if there are any access objects that are not RBAC
non_rbac_permissions = role.access.exclude(permission__contains='rbac:')

# if so, just delete the access object we know is RBAC, and leave the role
if non_rbac_permissions:
# this will still delete the resource definitions for the access object
rbac_permission.delete()
else:
# otherwise, we only have RBAC access objects, so delete everything
role.delete()


class Migration(migrations.Migration):

dependencies = [
('management', '0011_group_naming'),
]

operations = [
migrations.RunPython(remove_rbac_roles)
]
27 changes: 27 additions & 0 deletions rbac/management/migrations/0013_auto_20200128_2030.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 2.2.4 on 2020-01-28 20:30

from django.db import migrations


def remove_unnecessary_platform_default_role(apps, schema_editor):
role_names_to_remove = [
'Ansible Automation Access',
'Ansible Hub Access',
'Catalog Access',
'Remediations Access',
'Sources Access',
'Subscriptions Access',
]

Role = apps.get_model('management', 'Role')
Role.objects.filter(system=True, name__in=role_names_to_remove).delete()

class Migration(migrations.Migration):

dependencies = [
('management', '0012_remove_RonR_resources'),
]

operations = [
migrations.RunPython(remove_unnecessary_platform_default_role),
]
3 changes: 2 additions & 1 deletion rbac/management/querysets.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def get_group_queryset(request):
def get_role_queryset(request):
"""Obtain the queryset for roles."""
scope = request.query_params.get(SCOPE_KEY, ACCOUNT_SCOPE)
base_query = Role.objects.prefetch_related('access').annotate(policyCount=Count('policies', distinct=True))
base_query = Role.objects.prefetch_related('access').annotate(policyCount=Count('policies', distinct=True),
accessCount=Count('access', distinct=True))
if scope != ACCOUNT_SCOPE:
return get_object_principal_queryset(request, scope, Role,
**{'prefetch_lookups_for_ids': 'access',
Expand Down
6 changes: 4 additions & 2 deletions rbac/management/role/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ class RoleSerializer(serializers.ModelSerializer):
description = serializers.CharField(allow_null=True, required=False)
access = AccessSerializer(many=True)
policyCount = serializers.IntegerField(read_only=True)
accessCount = serializers.IntegerField(read_only=True)
applications = serializers.SerializerMethodField()
system = serializers.BooleanField(read_only=True)
platform_default = serializers.BooleanField(read_only=True)
Expand All @@ -81,7 +82,7 @@ class Meta:

model = Role
fields = ('uuid', 'name', 'description',
'access', 'policyCount',
'access', 'policyCount', 'accessCount',
'applications', 'system',
'platform_default', 'created',
'modified')
Expand Down Expand Up @@ -142,6 +143,7 @@ class RoleMinimumSerializer(serializers.ModelSerializer):
created = serializers.DateTimeField(read_only=True)
modified = serializers.DateTimeField(read_only=True)
policyCount = serializers.IntegerField(read_only=True)
accessCount = serializers.IntegerField(read_only=True)
applications = serializers.SerializerMethodField()
system = serializers.BooleanField(read_only=True)
platform_default = serializers.BooleanField(read_only=True)
Expand All @@ -151,7 +153,7 @@ class Meta:

model = Role
fields = ('uuid', 'name', 'description', 'created', 'modified', 'policyCount',
'applications', 'system', 'platform_default')
'accessCount', 'applications', 'system', 'platform_default')

def get_applications(self, obj):
"""Get the list of applications in the role."""
Expand Down
Loading

0 comments on commit 5e79962

Please sign in to comment.