Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Managers can create LDAP groups #11

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions jasmin_services/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,42 @@ def message_form_factory(sender, *roles):
})


def group_form_factory(*roles):
"""
Factory function that creates a group form for a set of roles.

The set of users is those with a valid, active grant for one of the roles.
"""
# The possible users are those that have an active, non-revoked, non-expired
# grant for the USER role for the service
queryset = get_user_model().objects.distinct() \
.filter(
grant__in = Grant.objects
.filter(
role__in = roles,
expires__gte = date.today(),
revoked = False
)
.filter_active()
).distinct()
return type(uuid.uuid4().hex, (forms.Form, ), {
'name' : forms.CharField(label = 'Name', required = True, max_length = 15),
'description' : forms.CharField(label = 'Description'),
'users' : forms.ModelMultipleChoiceField(
queryset = queryset,
label = 'Initial users'
),
})


class GroupForm(forms.Form):
"""
Form for creating a key LDAP group for an object store.
"""
name = forms.CharField(label = 'Name', required = True, max_length = 15)
description = forms.CharField(label = 'Description')


class DecisionForm(forms.Form):
"""
Form for making a decision on a request.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
{% user_has_service_perm service request.user 'jasmin_services.view_users_role' as view_users %}
{% user_has_service_perm service request.user 'jasmin_services.decide_request' as approver %}
{% user_has_service_perm service request.user 'jasmin_services.send_message_role' as send_message %}
{% user_has_service_perm service request.user 'jasmin_services.create_group_role' as create_group %}
mkjpryor-stfc marked this conversation as resolved.
Show resolved Hide resolved

{% with active=request.resolver_match.url_name %}
{% if view_users or approver or send_message %}
{% if view_users or approver or send_message or create_group %}
<ul class="nav nav-tabs">
<li {% if active == 'service_details' %}class="active"{% endif %}>
<a href="{% url 'jasmin_services:service_details' category=service.category.name service=service.name %}">Details</a>
Expand All @@ -31,6 +32,11 @@
</a>
</li>
{% endif %}
{% if create_group %}
<li {% if active == 'service_groups' %}class="active"{% endif %}>
<a href="{% url 'jasmin_services:service_groups' category=service.category.name service=service.name %}">Create group</a>
</li>
{% endif %}
</ul>
{% endif %}
{% endwith %}
60 changes: 60 additions & 0 deletions jasmin_services/templates/jasmin_services/service_group.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
{% extends 'layout.html' %}
{% load staticfiles bootstrap3 %}

{% block head_js_extra %}{{ block.super }}
<script src="{% static "jasmin_services/js/jquery.multi-select.js" %}" type="text/javascript"></script>
{% endblock %}

{% block stylesheets_extra %}{{ block.super }}
<link href="{% static "jasmin_services/css/multi-select.css" %}" media="screen" rel="stylesheet" type="text/css" />
{% endblock %}

{% block page_title %}Create a group{% endblock %}

{% block breadcrumbs %}
<ol class="breadcrumb">
<li>JASMIN Services</li>
<li>
<a href="{% url 'jasmin_services:service_list' category=service.category.name %}">{{ service.category }}</a>
</li>
<li class="active">{{ service.name }}</li>
</ol>
{% endblock %}

{% block content_header %}{{ block.super }}
<div class="row">
<div class="col-md-12">
{% include "jasmin_services/includes/service_tabs.html" %}
</div>
</div>
{% endblock %}

{% block content %}
<div class="col-md-8 col-md-offset-2">
<form method="POST" action="" class="form-horizontal" id="message-form">
{% csrf_token %}

{% bootstrap_form form layout='horizontal' %}

<div class="form-group">
<div class="col-sm-offset-3 col-sm-9">
<button name="submitted" type="submit" class="btn btn-primary" autocomplete="off">Create group</button>
</div>
</div>
</form>
</div>
<script type="text/javascript">
$('#message-form select').removeAttr('required').multiSelect({
selectableHeader : '<div class="ms-header"><strong>Available</strong> (<a id="select-all" href="#">select all</a>)</div>',
selectionHeader : '<div class="ms-header"><strong>Selected</strong> (<a id="select-none" href="#">remove all</a>)</div>',
});
$('#select-all').click(function() {
$('#message-form select').multiSelect('select_all');
return false;
});
$('#select-none').click(function() {
$('#message-form select').multiSelect('deselect_all');
return false;
});
</script>
{% endblock %}
1 change: 1 addition & 0 deletions jasmin_services/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
url(r'^requests/$', views.service_requests, name = 'service_requests'),
url(r'^users/$', views.service_users, name = 'service_users'),
url(r'^message/$', views.service_message, name = 'service_message'),
url(r'^groups/$', views.service_groups, name = 'service_groups'),
])),
url(r'^(?P<category>[\w-]+)/(?P<service>[\w-]+)/apply/(?P<role>[\w-]+)/$',
views.role_apply,
Expand Down
155 changes: 153 additions & 2 deletions jasmin_services/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
from django.template.loader import render_to_string
from django.db import transaction
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Permission
from django.contrib.auth.decorators import user_passes_test
from django.contrib.contenttypes.models import ContentType

from django.contrib.auth.decorators import login_required

from .models import Grant, Request, RequestState, Category, Service, Role
from .forms import DecisionForm, message_form_factory
from jasmin_metadata.models import Metadatum, Form
from .models import Grant, Request, RequestState, Category, Service, Role, Group, RoleObjectPermission, LdapGroupBehaviour, CedaLdapGroup
from .forms import DecisionForm, message_form_factory, group_form_factory


_log = logging.getLogger(__name__)
Expand Down Expand Up @@ -801,6 +804,154 @@ def service_message(request, service):
'form': form,
})

# from . import models as service_models
# CedaLdapGroup = getattr(models, 'CedaLdapGroup')
@require_http_methods(['GET', 'POST'])
@login_required
@with_service
def service_groups(request, service):
"""
Handler for ``/<category>/<service>/groups/``.

Responds to GET and POST. The user must have the ``create_groups_role``
permission for at least one role for the service.

Allows a user with suitable permissions to create an LDAP group which other
users of the service can apply for, depending which permissions they have
been granted.
"""
# Get the roles for which the user is allowed to create groups
# We allow the permission to be allocated for all services, per-service or per-role
permission = 'jasmin_services.create_group_role'
if request.user.has_perm(permission) or \
request.user.has_perm(permission, service):
user_roles = list(service.roles.all())
else:
user_roles = [
role
for role in service.roles.all()
if request.user.has_perm(permission, role)
]
# If the user has no permissions, send them back to the service details
# Note that we don't show this message if the user has been granted the
# permission for the service but there are no roles - in that case we
# just show nothing
if not user_roles:
messages.error(request, 'Insufficient permissions')
return redirect_to_service(service)
GroupForm = group_form_factory(*user_roles)
if request.method == 'POST':
form = GroupForm(request.POST)
if form.is_valid():
try:
name = form.cleaned_data['name']
description = form.cleaned_data['description']
member_uids = [u.account.uid for u in form.cleaned_data['users']]
LDAP_group_name = (service.name + '_' + name).replace('-', '_')
default_form = Form.objects.get(pk = settings.JASMIN_SERVICES['DEFAULT_METADATA_FORM'])

# Check if there is already a role with this name
try:
Role.objects.get(
service = service,
name = name,
)
raise Exception('Already exists')
except Role.DoesNotExist:
role = Role(
service = service,
name = name,
description = description,
metadata_form = default_form,
hidden = False,
)

# Check if there is already a ceda ldap group with this name
try:
CedaLdapGroup.objects.get(name = LDAP_group_name)
raise Exception('Already exists')
except CedaLdapGroup.DoesNotExist:
group = CedaLdapGroup(
name = LDAP_group_name,
description = description,
member_uids = member_uids,
)

# Save both after check so not to create only one
role.save()
group.save()

# Create the LDAP group behaviour
ldap_group_behaviour = LdapGroupBehaviour.objects.create(
ldap_model = CedaLdapGroup,
group_name = LDAP_group_name,
)
role.behaviours.add(ldap_group_behaviour)

# Create relevant role_object_permissions to the manager and deputy
permissions = [
Permission.objects.get(
content_type = ContentType.objects.get_for_model(Role),
codename = 'view_users_role',
),
Permission.objects.get(
content_type = ContentType.objects.get_for_model(Role),
codename = 'send_message_role',
),
Permission.objects.get(
content_type = ContentType.objects.get_for_model(Request),
codename = 'decide_request',
),
]

management_roles = [
Role.objects.get(
service = service,
name = 'MANAGER',
),
Role.objects.get(
service = service,
name = 'DEPUTY',
),
]

for management_role in management_roles:
RoleObjectPermission.objects.bulk_create([
RoleObjectPermission(
role = management_role,
permission = permission,
content_type = ContentType.objects.get_for_model(role),
object_pk = role.pk
)
for permission in permissions
])

messages.success(request, 'Group created')
return redirect_to_service(service)
except Exception as e:
print(e)
if str(e) == 'Already exists':
messages.error(request, 'Group with this name already exists')
else:
messages.error(request, 'Error creating group')

else:
messages.error(request, 'Error with one or more fields')
else:
form = GroupForm()
templates = [
'jasmin_services/{}/{}/service_group.html'.format(
service.category.name,
service.name
),
'jasmin_services/{}/service_group.html'.format(service.category.name),
'jasmin_services/service_group.html',
]
return render(request, templates, {
'service': service,
'form': form,
})


@require_safe
def reverse_dns_check(request):
Expand Down