diff --git a/.pylintrc b/.pylintrc index 0d8266ca3808f668e998b644118a891288d4340e..038648ffa5353a85c687df0372aadf26ad72ec66 100644 --- a/.pylintrc +++ b/.pylintrc @@ -216,7 +216,7 @@ contextmanager-decorators=contextlib.contextmanager # List of members which are set dynamically and missed by pylint inference # system, and so shouldn't trigger E1101 when accessed. Python regular # expressions are accepted. -generated-members=query +generated-members=query,UniqueConstraint # Tells whether missing members accessed in mixin class should be ignored. A # mixin class is detected if its name ends with "mixin" (case insensitive). diff --git a/uffd/role/models.py b/uffd/role/models.py index 3b4ca85fe495aaecd060c42eed6c3c2d8ec576a6..596f2e0d85a678a10facacea916249d70e9e6740 100644 --- a/uffd/role/models.py +++ b/uffd/role/models.py @@ -1,25 +1,35 @@ +from operator import attrgetter + from sqlalchemy import Column, String, Integer, Text, ForeignKey from sqlalchemy.orm import relationship from sqlalchemy.ext.declarative import declared_attr from uffd.database import db -from uffd.user.models import User, Group +from uffd.user import User, Group class Role(db.Model): __tablename__ = 'role' id = Column(Integer(), primary_key=True, autoincrement=True) name = Column(String(32), unique=True) description = Column(Text()) - members = relationship("RoleUser", backref="role") - groups = relationship("RoleGroup", backref="role") + members = relationship("RoleUser", backref="role", cascade="all, delete-orphan") + groups = relationship("RoleGroup", backref="role", cascade="all, delete-orphan") def __init__(self, name='', description=''): self.name = name self.description = description + def group_dns(self): + return map(attrgetter('dn'), self.groups) + def member_dns(self): + return map(attrgetter('dn'), self.members) + class LdapMapping(): id = Column(Integer(), primary_key=True, autoincrement=True) dn = Column(String(128)) + __table_args__ = ( + db.UniqueConstraint('dn', 'role_id'), + ) @declared_attr def role_id(self): return Column(ForeignKey('role.id')) diff --git a/uffd/role/templates/role.html b/uffd/role/templates/role.html index 45672dafdedb5d0e451ec9e13d60b923f3c51487..cdddccee9078e01d64f9e3540deae0b01e418b6e 100644 --- a/uffd/role/templates/role.html +++ b/uffd/role/templates/role.html @@ -15,6 +15,47 @@ <small class="form-text text-muted"> </small> </div> + <div class="form-group col"> + <p> + Included groups + </p> + <table class="table table-striped table-sm"> + <thead> + <tr> + <th scope="col"></th> + <th scope="col">name</th> + <th scope="col">description</th> + </tr> + </thead> + <tbody> + {% for group in groups|sort(attribute="name") %} + <tr id="group-{{ group.gid }}"> + <td> + <div class="form-check"> + <input class="form-check-input" type="checkbox" id="group-{{ group.gid }}-checkbox" name="group-{{ group.gid }}" value="1" aria-label="enabled" {% if group.dn in role.group_dns() %}checked{% endif %}> + </div> + </td> + <td> + <a href="{{ url_for("group.show", gid=group.gid) }}"> + {{ group.name }} + </a> + </td> + <td> + {{ group.description }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + </div> + <div class="form-group col"> + <p> + Members + </p> + {% for member in role.members %} + {{ member.dn }} + {% endfor %} + </div> <div class="form-group col"> <button type="submit" class="btn btn-primary"><i class="fa fa-save" aria-hidden="true"></i> Save</button> diff --git a/uffd/role/views.py b/uffd/role/views.py index 9863d56554092d170df68fe3072785bbf7df0a25..737d1c6b17056cbca3a7f0d71d5693802161b317 100644 --- a/uffd/role/views.py +++ b/uffd/role/views.py @@ -2,7 +2,8 @@ from flask import Blueprint, render_template, request, url_for, redirect, flash, from uffd.navbar import register_navbar from uffd.csrf import csrf_protect -from uffd.role.models import Role +from uffd.role.models import Role, RoleGroup +from uffd.user import Group from uffd.session import get_current_user, login_required, is_valid_session from uffd.database import db @@ -29,7 +30,8 @@ def show(roleid=False): role = Role() else: role = Role.query.get_or_404(roleid) - return render_template('role.html', role=role) + groups = Group.from_ldap_all() + return render_template('role.html', role=role, groups=groups) @bp.route("/<int:roleid>/update", methods=['POST']) @bp.route("/new", methods=['POST']) @@ -44,7 +46,20 @@ def update(roleid=False): role = session.query(Role).get_or_404(roleid) role.name = request.values['name'] role.description = request.values['description'] - print(role) + + groups = Group.from_ldap_all() + role_group_dns = list(role.group_dns()) + for group in groups: + if request.values.get('group-{}'.format(group.gid), False): + if group.dn in role_group_dns: + continue + newmapping = RoleGroup() + newmapping.dn = group.dn + newmapping.role = role + session.add(newmapping) + elif group.dn in role_group_dns: + session.delete(RoleGroup.query.filter_by(role_id=role.id, dn=group.dn).one()) + session.commit() return redirect(url_for('role.index')) diff --git a/uffd/user/models.py b/uffd/user/models.py index fa3dbf6695bc760866c463fa0ee255f84059fe6e..7da2cca0bde9e1970110ff1189bafb6f4199c9d7 100644 --- a/uffd/user/models.py +++ b/uffd/user/models.py @@ -106,12 +106,13 @@ class User(): return True class Group(): - def __init__(self, gid=None, name='', members=None, description=''): + def __init__(self, gid=None, name='', members=None, description='', dn=None): self.gid = gid self.name = name self.members_ldap = members self._members = None self.description = description + self.dn = dn @classmethod def from_ldap(cls, ldapobject): @@ -120,6 +121,7 @@ class Group(): name=ldapobject['cn'].value, members=ldap.get_ldap_array_attribute_safe(ldapobject, 'uniqueMember'), description=ldap.get_ldap_attribute_safe(ldapobject, 'description') or '', + dn=ldapobject.entry_dn, ) @classmethod @@ -130,6 +132,15 @@ class Group(): return None return Group.from_ldap(conn.entries[0]) + @classmethod + def from_ldap_all(cls): + conn = ldap.get_conn() + conn.search(current_app.config["LDAP_BASE_GROUPS"], '(objectclass=groupOfUniqueNames)') + groups = [] + for i in conn.entries: + groups.append(Group.from_ldap(i)) + return groups + def to_ldap(self, new): pass diff --git a/uffd/user/views_group.py b/uffd/user/views_group.py index 04351d4159f21f3df4e7536eb8a4026c3684877d..8c1c805eeb00ca6fe43e054bb97aeb590b130df3 100644 --- a/uffd/user/views_group.py +++ b/uffd/user/views_group.py @@ -20,12 +20,7 @@ def group_acl_check(): @bp.route("/") @register_navbar('Groups', icon='layer-group', blueprint=bp, visible=group_acl_check) def index(): - conn = get_conn() - conn.search(current_app.config["LDAP_BASE_GROUPS"], '(objectclass=groupOfUniqueNames)') - groups = [] - for i in conn.entries: - groups.append(Group.from_ldap(i)) - return render_template('group_list.html', groups=groups) + return render_template('group_list.html', groups=Group.from_ldap_all()) @bp.route("/<int:gid>") def show(gid):