##// END OF EJS Templates
i18n: updated translation for Polish...
i18n: updated translation for Polish Currently translated at 56.5% (614 of 1087 strings)

File last commit:

r8068:22b40db4 default
r8092:7fef5132 default
Show More
ldap_sync.py
261 lines | 8.4 KiB | text/x-python | PythonLexer
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 # -*- coding: utf-8 -*-
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
Mads Kiilerich
cleanup: make module self-naming consistent...
r5376 kallithea.bin.ldap_sync
~~~~~~~~~~~~~~~~~~~~~~~
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
LDAP sync script
Bradley M. Kuhn
RhodeCode GmbH is not the sole author of this work
r4211 This file was forked by the Kallithea project in July 2014.
Original author and date, and relevant copyright and licensing information is below:
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 :created_on: Mar 06, 2013
:author: marcink
Bradley M. Kuhn
RhodeCode GmbH is not the sole author of this work
r4211 :copyright: (c) 2013 RhodeCode GmbH, and others.
Bradley M. Kuhn
Correct licensing information in individual files....
r4208 :license: GPLv3, see LICENSE.md for more details.
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """
Mads Kiilerich
future: use Python print function
r7750 from __future__ import print_function
Mads Kiilerich
py3: migrate from urllib2 to urllib...
r8068 import urllib.request
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 import uuid
Mads Kiilerich
py3: support new stdlib module names configparser, urllib and http lib...
r8067 from configparser import ConfigParser
Mads Kiilerich
scripts: initial run of import cleanup using isort
r7718
import ldap
Thomas De Schampheleire
bin/ldap_sync: revert commit 04dee6fdfdff...
r7866
Mads Kiilerich
lib: clean up ext_json and how it is used - avoid monkey patching...
r7987 from kallithea.lib import ext_json
from kallithea.lib.utils2 import ascii_bytes
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
config = ConfigParser()
config.read('ldap_sync.conf')
class InvalidResponseIDError(Exception):
""" Request and response don't have the same UUID. """
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 class ResponseError(Exception):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 """ Response has an error, something went wrong with request execution. """
class UserAlreadyInGroupError(Exception):
""" User is already a member of the target group. """
class UserNotInGroupError(Exception):
""" User is not a member of the target group. """
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 class API(object):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def __init__(self, url, key):
self.url = url
self.key = key
def get_api_data(self, uid, method, args):
"""Prepare dict for API post."""
return {
"id": uid,
"api_key": self.key,
"method": method,
"args": args
}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 def post(self, method, args):
Bradley M. Kuhn
General renaming to Kallithea
r4212 """Send a generic API post to Kallithea.
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
This will generate the UUID for validation check after the
response is returned. Handle errors and get the result back.
"""
uid = str(uuid.uuid1())
data = self.get_api_data(uid, method, args)
Mads Kiilerich
lib: clean up ext_json and how it is used - avoid monkey patching...
r7987 data = ascii_bytes(ext_json.dumps(data))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 headers = {'content-type': 'text/plain'}
Mads Kiilerich
py3: migrate from urllib2 to urllib...
r8068 req = urllib.request.Request(self.url, data, headers)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
Mads Kiilerich
py3: migrate from urllib2 to urllib...
r8068 response = urllib.request.urlopen(req)
Mads Kiilerich
lib: clean up ext_json and how it is used - avoid monkey patching...
r7987 response = ext_json.load(response)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
if uid != response["id"]:
raise InvalidResponseIDError("UUID does not match.")
if response["error"] is not None:
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 raise ResponseError(response["error"])
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
return response["result"]
def create_group(self, name, active=True):
Bradley M. Kuhn
General renaming to Kallithea
r4212 """Create the Kallithea user group."""
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 args = {
"group_name": name,
"active": str(active)
}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 self.post("create_user_group", args)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def add_membership(self, group, username):
"""Add specific user to a group."""
args = {
"usersgroupid": group,
"userid": username
}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 result = self.post("add_user_to_user_group", args)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 if not result["success"]:
raise UserAlreadyInGroupError("User %s already in group %s." %
(username, group))
def remove_membership(self, group, username):
"""Remove specific user from a group."""
args = {
"usersgroupid": group,
"userid": username
}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 result = self.post("remove_user_from_user_group", args)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 if not result["success"]:
raise UserNotInGroupError("User %s not in group %s." %
(username, group))
def get_group_members(self, name):
"""Get the list of member usernames from a user group."""
args = {"usersgroupid": name}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 members = self.post("get_user_group", args)['members']
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 member_list = []
for member in members:
member_list.append(member["username"])
return member_list
def get_group(self, name):
"""Return group info."""
args = {"usersgroupid": name}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 return self.post("get_user_group", args)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def get_user(self, username):
"""Return user info."""
args = {"userid": username}
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 return self.post("get_user", args)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
class LdapClient(object):
def __init__(self, uri, user, key, base_dn):
self.client = ldap.initialize(uri, trace_level=0)
self.client.set_option(ldap.OPT_REFERRALS, 0)
self.client.simple_bind(user, key)
self.base_dn = base_dn
Mads Kiilerich
bin: let ldap_sync use explicit .close() instead of relying on unbinding in .__del__()...
r7784 def close(self):
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 self.client.unbind()
def get_groups(self):
"""Get all the groups in form of dict {group_name: group_info,...}."""
searchFilter = "objectClass=groupOfUniqueNames"
result = self.client.search_s(self.base_dn, ldap.SCOPE_SUBTREE,
searchFilter)
groups = {}
for group in result:
groups[group[1]['cn'][0]] = group[1]
return groups
def get_group_users(self, groups, group):
"""Returns all the users belonging to a single group.
Based on the list of groups and memberships, returns all the
users belonging to a single group, searching recursively.
"""
users = []
for member in groups[group]["uniqueMember"]:
member = self.parse_member_string(member)
if member[0] == "uid":
users.append(member[1])
elif member[0] == "cn":
users += self.get_group_users(groups, member[1])
return users
def parse_member_string(self, member):
"""Parses the member string and returns a touple of type and name.
Unique member can be either user or group. Users will have 'uid' as
prefix while groups will have 'cn'.
"""
member = member.split(",")[0]
return member.split('=')
class LdapSync(object):
def __init__(self):
self.ldap_client = LdapClient(config.get("default", "ldap_uri"),
config.get("default", "ldap_user"),
config.get("default", "ldap_key"),
config.get("default", "base_dn"))
Mads Kiilerich
ldap_sync: fix old typo in branded variable naming
r5377 self.kallithea_api = API(config.get("default", "api_url"),
config.get("default", "api_key"))
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
def update_groups_from_ldap(self):
Bradley M. Kuhn
General renaming to Kallithea
r4212 """Add all the groups from LDAP to Kallithea."""
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 added = existing = 0
groups = self.ldap_client.get_groups()
for group in groups:
try:
domruf
ldap_sync: change create_repo_group to create_group...
r6588 self.kallithea_api.create_group(group)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 added += 1
except Exception:
existing += 1
return added, existing
def update_memberships_from_ldap(self, group):
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 """Update memberships based on the LDAP groups."""
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 groups = self.ldap_client.get_groups()
group_users = self.ldap_client.get_group_users(groups, group)
# Delete memberships first from each group which are not part
# of the group any more.
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 members = self.kallithea_api.get_group_members(group)
for member in members:
if member not in group_users:
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 try:
Mads Kiilerich
ldap_sync: fix old typo in branded variable naming
r5377 self.kallithea_api.remove_membership(group,
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 member)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 except UserNotInGroupError:
pass
# Add memberships.
for member in group_users:
try:
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 self.kallithea_api.add_membership(group, member)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 except UserAlreadyInGroupError:
# TODO: handle somehow maybe..
pass
Mads Kiilerich
bin: let ldap_sync use explicit .close() instead of relying on unbinding in .__del__()...
r7784 def close(self):
self.ldap_client.close()
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
if __name__ == '__main__':
sync = LdapSync()
Mads Kiilerich
future: use Python print function
r7750 print(sync.update_groups_from_ldap())
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187
for gr in sync.ldap_client.get_groups():
# TODO: exception when user does not exist during add membership...
# How should we handle this.. Either sync users as well at this step,
# or just ignore those who don't exist. If we want the second case,
# we need to find a way to recognize the right exception (we always get
Bradley M. Kuhn
Rename helper tools (and fix inconsistent naming)
r4189 # ResponseError with no error code so maybe by return msg (?)
Bradley M. Kuhn
Second step in two-part process to rename directories....
r4187 sync.update_memberships_from_ldap(gr)
Mads Kiilerich
bin: let ldap_sync use explicit .close() instead of relying on unbinding in .__del__()...
r7784
sync.close()