Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
File renamed without changes.
File renamed without changes.
41 changes: 41 additions & 0 deletions app/static/js/search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Adapted from http://jsfiddle.net/ukW2C/3/
$("#search").keyup(function () {
//split the current value of search
var data = this.value.split(" ");

//create a jquery object of the rows
var jo = $("#users").find("tr");
if (this.value == "") {
jo.show();
$("#search-group").removeClass("has-error");
$("#search-group").removeClass("has-success");
return;
}
//hide all the rows
jo.hide();

//Recusively filter the jquery object to get results.
filtered = jo.filter(function (i, v) {
var $t = $(this);
for (var d = 0; d < data.length; ++d) {
if ($t.is(":contains('" + data[d] + "')")) {
return true;
}
}
return false;
});
//show the rows that match.
filtered.show();

//update the search bar validation status
if (filtered.length) {
$("#search-group").addClass("has-success");
$("#search-group").removeClass("has-error");
} else {
$("#search-group").addClass("has-error");
$("#search-group").removeClass("has-success");
}
}).focus(function () {
this.value = "";
$(this).unbind('focus');
});
3 changes: 2 additions & 1 deletion app/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% block styles %}
{{super()}}
<link rel="stylesheet"
href="{{url_for('static', filename='main.css')}}">
href="{{url_for('static', filename='css/main.css')}}">

{% endblock %}
{% block navbar %}
Expand All @@ -29,6 +29,7 @@
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Admin <span class="caret"></span></a>
<ul class="dropdown-menu">
<li><a href="{{url_for('user.list_users')}}">Users</a></li>
<li><a href="{{url_for('user.list_groups')}}">Groups</a></li>
<li><a href="{{url_for('oauth2.clients')}}">OAuth2 Applications</a></li>
</ul>
</li>
Expand Down
2 changes: 1 addition & 1 deletion app/templates/confirm.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ <h1 class="form-confirmation-heading">{% if data.prompt %}{{ data.prompt }}{% el
{{ wtf.form_errors(form) }}
{% if data.back %}
<div class="btn-group pull-right">
<a class="btn btn-lg btn-default" href="{{ url_for(data.back) }}">
<a class="btn btn-lg btn-default" href="{{ data.back }}">
<span class="glyphicon glyphicon-arrow-left" aria-hidden="true"></span> Back
</a>
{{ form.submit(class='btn btn-lg btn-primary btn-danger', value=data.action) }}
Expand Down
64 changes: 61 additions & 3 deletions app/user/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
from wtforms.fields.html5 import EmailField
from app.views import confirm, is_safe_url
from . import groups_required, user_blueprint
from .models import User
from .models import User, Group
from ldap3.core.exceptions import LDAPAttributeOrValueExistsResult

class UserEditForm(FlaskForm):
username = StringField('Username')
Expand All @@ -26,10 +27,21 @@ class UserEditForm(FlaskForm):
@groups_required('admin')
def list_users():
users = User.query()
return render_template('admin/listUsers.html',
return render_template('admin/user/list.html',
users = users
)

@user_blueprint.route('/admin/user/<string:username>')
@login_required
@groups_required('admin')
def profile(username):
user = User.get(username)
if not user:
flash('Invalid user name!', 'error')
return redirect(url_for('user.list_users'))
all_groups = Group.query()
return render_template('admin/group/profile.html', user=user, groups=all_groups)

@user_blueprint.route('/admin/user/<string:username>/delete', methods=['GET', 'POST'])
@login_required
@confirm(title='Delete User?',
Expand Down Expand Up @@ -85,7 +97,53 @@ def edit_user(username, back_url = None):
form.surname.data = user.surname
form.mail.data = user.mail

return render_template('admin/editUser.html',
return render_template('admin/user/edit.html',
form = form,
user = user
)

@user_blueprint.route('/admin/user/<string:username>/join/<string:group_name>')
@login_required
@groups_required('admin')
def join(username, group_name):
try:
group = Group.get(group_name)
if not group:
raise AttributeError("group does not exist")
group.join(User.get(username))
group.save()
except LDAPAttributeOrValueExistsResult:
flash("{} is already a member of {}".format(username, group_name))
except AttributeError:
abort(404)
return redirect(url_for('user.profile', username=username))

@user_blueprint.route('/admin/user/<string:username>/leave/<string:group_name>', methods=['GET', 'POST'])
@login_required
@groups_required('admin')
@confirm(title='Leave group?',
prompt='Are you sure you want to remove this user from the group?',
action='Remove')
def leave(username, group_name):
group = Group.get(group_name)
if not group:
raise AttributeError("group does not exist")
group.leave(User.get(username))
group.save()
return redirect(url_for('user.profile', username=username))

@user_blueprint.route('/admin/group/<string:group_name>')
@login_required
@groups_required('admin')
def group_page(group_name):
group = Group.get(group_name)
if not group:
flash('Invalid group name!', 'error')
return redirect(url_for('user.list_groups'))
return render_template('admin/group/profile.html', group=group)

@user_blueprint.route('/admin/groups')
@login_required
@groups_required('admin')
def list_groups():
return render_template('admin/group/list.html', groups=Group.query())
2 changes: 1 addition & 1 deletion app/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def create(username, givenName, surname, password, mail = None):

def delete(self):
"""
Remove a user.
Delete a group.
"""
for group in self.groups:
group.leave(self)
Expand Down
37 changes: 37 additions & 0 deletions app/user/templates/admin/group/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{% extends "base.html" %}

{% block title %}Groups{% endblock %}

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="{{ url_for('static', filename='js/search.js') }}"></script>
{% endblock %}

{% block content %}
<div class="container">
<h1>Groups</h1>

<div id="search-group" class="form-group has-feedback">
<label class="control-label sr-only" for="search">Search for users</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></span>
<input id="search" type="text" class="form-control input-lg" placeholder="Search">
</div>
</div>

<table class='table'>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
<tbody id="users">
{% for group in groups %}
<tr>
<td><a href="{{ url_for('user.group_page', group_name=group.group_name) }}">{{ group.group_name }}</a></td>
<td>{{ group.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
41 changes: 41 additions & 0 deletions app/user/templates/admin/group/profile.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{% extends "base.html" %}

{% block title %}{{ group.group_name }}{% endblock %}

{% block content %}
<div class="container">
<div class="page-header">
<h1>{{ group.group_name }} <small>{{ group.description }}</small></h1>
</div>

<div class="panel panel-primary">
<div class="panel-heading"><h2>General information</h2></div>
<div class="panel-body form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">LDAP DN</label>
<div class="col-sm10"><p class="form-control-static">{{ group.dn }}</p></div>
</div>
</div>
</div>

<div class="panel panel-default">
<div class="panel-heading">
<h2 class="clearfix">Members</h2>
</div>
<ul class="list-group">
{% for member in group.members %}
<li class="list-group-item">
<h4 class="list-group-item-heading clearfix">{{ member.full_name }}
<small><a href="{{ url_for('user.profile', username=member.username) }}">{{ member.username }}</a></small>
<a class="btn btn-default pull-right"
href="{{ url_for('user.leave', username=member.username, group_name=group.group_name) }}">
<span class="glyphicon glyphicon-log-out" aria-hidden="true"></span>
Remove</a>
</h4>
<p class="list-group-item-text">{{ member.mail }}</p>
</li>
{% endfor %}
</ul>
</div>
</div>
{% endblock %}
92 changes: 0 additions & 92 deletions app/user/templates/admin/listUsers.html

This file was deleted.

50 changes: 50 additions & 0 deletions app/user/templates/admin/user/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{% extends "base.html" %}

{% block title %}Users{% endblock %}

{% block scripts %}
{{ super() }}
<script type="text/javascript" src="{{ url_for('static', filename='js/search.js') }}"></script>
{% endblock %}

{% block content %}
<div class="container">
<h1>Users</h1>

<div id="search-group" class="form-group has-feedback">
<label class="control-label sr-only" for="search">Search for users</label>
<div class="input-group">
<span class="input-group-addon"><span class="glyphicon glyphicon-search" aria-hidden="true"></span></span>
<input id="search" type="text" class="form-control input-lg" placeholder="Search">
</div>
</div>

<table class='table'>
<tr>
<th>Username</th>
<th>Full name</th>
<th>E-Mail</th>
<th>Actions</th>
</tr>
<tbody id="users">
{% for user in users %}
<tr>
<td><a href="{{ url_for('user.profile', username=user.username) }}">{{ user.username }}</a></td>
<td>{{ user.full_name }}</td>
<td>{{ user.mail }}</td>
<td>
<div class="btn-group" role="group">
<a class="btn-default btn" href="{{url_for('user.edit_user', username=user.username)}}">
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span>&nbsp;Edit
</a>
<a class="btn-danger btn" href="{{url_for('user.delete_user', username=user.username)}}">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>&nbsp;Delete
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
Loading