263 lines
11 KiB
Python
263 lines
11 KiB
Python
#!/usr/bin/env python
|
|
|
|
from ldap3 import Connection, Server, ANONYMOUS, SIMPLE, SYNC, ASYNC, core
|
|
from getpass import getpass
|
|
import configparser
|
|
import logging
|
|
import argparse
|
|
import os
|
|
import requests
|
|
import urllib3
|
|
import json
|
|
import mysql.connector
|
|
|
|
def request(resource, seafileURL, seafileToken, method='GET', data=None):
|
|
if data is None:
|
|
data = ''
|
|
else:
|
|
data = json.dumps(data)
|
|
url = '{0}/api/v2.1/{1}'.format(seafileURL, resource)
|
|
logger.debug('Request URL: {}'.format(url))
|
|
logger.debug('Request Data: {}'.format(data))
|
|
r = requests.request(
|
|
method,
|
|
url,
|
|
data=data,
|
|
headers={'Content-type': 'application/json','Accept': 'application/json; charset=utf-8; indent=4', 'Authorization': 'Token {0}'.format(seafileToken)},
|
|
)
|
|
logger.debug('Request Status Code: {}'.format(r.status_code))
|
|
if r.ok:
|
|
try:
|
|
logger.debug('Request Returned JSON: {}'.format(r.json()))
|
|
return {'ok': r.ok, 'status_code': r.status_code, 'response': r.json()}
|
|
except:
|
|
logger.debug('Request Returned Text: {}'.format(r.text))
|
|
return {'ok': r.ok, 'status_code': r.status_code, 'response': r.text}
|
|
raise ValueError(r)
|
|
|
|
#def
|
|
parser = argparse.ArgumentParser(description='Sync LDAP with Seafile')
|
|
#group = parser.add_mutually_exclusive_group()
|
|
#group.add_argument('-e', '--encrypt', action='store_true', help='encrypt')
|
|
#group.add_argument('-d', '--decrypt', action='store_true', help='decrypt')
|
|
#parser.add_argument('-t', '--text', type=str, help='text to encrypt/decrypt')
|
|
#group2 = parser.add_mutually_exclusive_group()
|
|
#group2.add_argument('-t', '--text', type=str, help='text to encrypt/decrypt')
|
|
#group2.add_argument('-f', '--file', type=str, help='file to encrypt/decrypt, will create a .pynacl file when encrypting, and requires .pynacl file to decrypt')
|
|
parser.add_argument('-v', '--verbose', action='store_true', help='increase log level to debug')
|
|
args = parser.parse_args()
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
logFormatter = logging.Formatter('%(asctime)s - [%(levelname)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
|
logger.setLevel(logging.INFO)
|
|
if args.verbose:
|
|
logger.setLevel(logging.DEBUG)
|
|
else:
|
|
logger.setLevel(logging.INFO)
|
|
|
|
#fileHandler = logging.FileHandler("/tmp/seafile-ldap.log")
|
|
#fileHandler.setFormatter(logFormatter)
|
|
#logger.addHandler(fileHandler)
|
|
|
|
consoleHandler = logging.StreamHandler()
|
|
consoleHandler.setFormatter(logFormatter)
|
|
logger.addHandler(consoleHandler)
|
|
|
|
# get directory of script
|
|
cwd = os.path.dirname(os.path.realpath(__file__))
|
|
configPath = os.path.join(cwd, 'config.ini')
|
|
|
|
# import the config file
|
|
logger.debug("Starting to read the config.ini file.")
|
|
logger.debug("Using file {0}.".format(configPath))
|
|
if os.path.exists(configPath):
|
|
config = configparser.ConfigParser()
|
|
config.read(configPath)
|
|
else:
|
|
logger.critical("Unable to find/read config file. Please ensure the config.ini is in the same directory as this script and readable.")
|
|
exit(1)
|
|
|
|
seafileToken = config['Seafile']['token']
|
|
ccnetPath = config['Seafile']['ccnetPath']
|
|
logger.debug("Seafile Token: {0}".format(seafileToken))
|
|
logger.debug("Seafile ccnet.conf File Path: {0}".format(ccnetPath))
|
|
logger.debug("Finished reading the config.ini file.")
|
|
logger.debug("Starting to read the ccnet.conf file.")
|
|
if os.path.exists(ccnetPath):
|
|
ccnetConfig = configparser.ConfigParser()
|
|
ccnetConfig.read(ccnetPath)
|
|
else:
|
|
logger.critical("Unable to find/read ccnet.conf file. Please ensure the ccnet.conf path is correct in the config.ini.")
|
|
exit(2)
|
|
|
|
# Seafile url
|
|
seafileURL = ccnetConfig['General']['SERVICE_URL']
|
|
|
|
# DB config
|
|
dbEngine = ccnetConfig['Database']['ENGINE']
|
|
dbHost = ccnetConfig['Database']['HOST']
|
|
dbPort = ccnetConfig['Database'].getint('PORT')
|
|
dbUser = ccnetConfig['Database']['USER']
|
|
dbPassword = ccnetConfig['Database']['PASSWD']
|
|
dbName = ccnetConfig['Database']['DB']
|
|
dbCharset = ccnetConfig['Database']['CONNECTION_CHARSET']
|
|
logger.debug("DB Engine: {0}, DB Host: {1}, DB Port: {2}, DB User: {3}, DB Name: {4}, DB Connection Charset: {5}".format(dbEngine, dbHost, dbPort, dbUser, dbName, dbCharset))
|
|
|
|
# ldap Config
|
|
ldapHost = ccnetConfig['LDAP']['HOST']
|
|
#ldapPort = ccnetConfig['LDAP SERVER'].getint('port')
|
|
#ldapSSL = ccnetConfig['LDAP SERVER'].getboolean('ssl')
|
|
ldapBase = ccnetConfig['LDAP']['BASE']
|
|
ldapUserDN = ccnetConfig['LDAP']['USER_DN']
|
|
ldapUserPassword = ccnetConfig['LDAP']['PASSWORD']
|
|
ldapFilter = ccnetConfig['LDAP']['FILTER']
|
|
logger.debug("LDAP Host: {0}, LDAP Base: {1}, LDAP User DN: {2}, LDAP Filter: {3}".format(ldapHost, ldapBase, ldapUserDN, ldapFilter))
|
|
|
|
logger.debug("Finished reading the ccnet.conf file.")
|
|
|
|
# setup the server
|
|
ldapServer = Server(ldapHost)
|
|
logger.debug("Setup LDAP server connection uri: {0}".format(ldapServer))
|
|
try:
|
|
ldap = Connection(ldapServer, ldapUserDN, ldapUserPassword, auto_bind=True)
|
|
except core.exceptions.LDAPBindError as e:
|
|
logger.critical("LDAP Bind Failed. {0}".format(e))
|
|
exit()
|
|
logger.debug("Bind successful.")
|
|
|
|
# Get seafile users from LDAP
|
|
logger.debug("Searching for users that have a email address, are enabled, and in the {} group.".format(ldapFilter))
|
|
ldap.search(ldapBase, '(&(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2))({0}))'.format(ldapFilter), attributes=['*'])
|
|
logger.debug("Found {0} LDAP users.".format(len(ldap.entries)))
|
|
ldapUsers = ldap.entries
|
|
for user in ldapUsers:
|
|
logger.debug("User: {0} - Email: {1} - UserDN: {2}".format(user.name, user.mail, user.distinguishedName))
|
|
|
|
# Connect to DB
|
|
dbconfig = {
|
|
'user': dbUser,
|
|
'password': dbPassword,
|
|
'host': dbHost,
|
|
'port': dbPort,
|
|
'database': dbName,
|
|
'charset': dbCharset,
|
|
'raise_on_warnings': True
|
|
}
|
|
|
|
|
|
cnx = mysql.connector.connect(**dbconfig)
|
|
cursor = cnx.cursor()
|
|
# Starting query for sql ldap users
|
|
logger.debug("Starting SQL query for LDAPUsers")
|
|
query = ("SELECT * FROM LDAPUsers")
|
|
logger.debug("Query: {0}".format(query))
|
|
cursor.execute(query)
|
|
sqlLDAPusers = cursor.fetchall()
|
|
logger.debug("Found {0} SQL LDAP users".format(cursor.rowcount))
|
|
for user in sqlLDAPusers:
|
|
logger.debug("User: {0} - Active: {1}".format(user[1], bool(user[4])))
|
|
logger.debug("Finished SQL query for LDAPUsers")
|
|
cnx.close()
|
|
|
|
|
|
# Loop through the ldap users and make sure they are in the sql ldap users table
|
|
# if they are not in the sql table, insert a new row to add them
|
|
# if they are disabled in the sql table, enable them
|
|
for ldapUser in ldapUsers:
|
|
logger.debug("Checking if LDAP user {0} is in SQl Table".format(ldapUser.mail))
|
|
cnx = mysql.connector.connect(**dbconfig)
|
|
cursor = cnx.cursor()
|
|
query = "SELECT * FROM LDAPUsers WHERE email = '{0}'".format(ldapUser.mail)
|
|
logger.debug("Query: {0}".format(query))
|
|
cursor.execute(query)
|
|
sqlLDAPuser = cursor.fetchall()
|
|
row_count = cursor.rowcount
|
|
logger.debug("Found {0} SQL LDAP user".format(row_count))
|
|
cnx.close()
|
|
if row_count == 1:
|
|
# User is in the sql table
|
|
# are they active
|
|
is_active = bool(sqlLDAPuser[0][4])
|
|
# log the results
|
|
logger.debug("LDAP User {0} is already in the SQL Table, Is Active: {1}".format(ldapUser.mail, is_active))
|
|
# if user is not active, they should be
|
|
if not is_active:
|
|
logger.info("User {0} is NOT active in Seafile".format(ldapUser.mail))
|
|
# call the api to enable the user in seafile
|
|
enableSeafileUser = request('admin/users/{0}/'.format(ldapUser.mail), seafileURL, seafileToken, "PUT", {"is_active": "true"})['response']
|
|
if enableSeafileUser['is_active']:
|
|
logger.info("User {0} was set to active in Seafile".format(ldapUser.mail))
|
|
else:
|
|
logger.error("There was an error setting user {0} to active in Seafile".format(ldapUser.mail))
|
|
# user is not in the SQL table
|
|
else:
|
|
logger.info("LDAP User {0} is NOT in the SQL Table".format(ldapUser.mail))
|
|
# add user to ldap table
|
|
cnx = mysql.connector.connect(**dbconfig)
|
|
cursor = cnx.cursor()
|
|
query = "INSERT INTO LDAPUsers (email, password, is_staff, is_active) VALUES ('{0}', '', {1}, {2})".format(ldapUser.mail, 0, 1)
|
|
logger.debug("Query: {0}".format(query))
|
|
cursor.execute(query)
|
|
cnx.commit()
|
|
row_count = cursor.rowcount
|
|
if row_count == 1:
|
|
logger.info("LDAP user {0} was added to the SQL Table".format(ldapUser.mail))
|
|
else:
|
|
logger.error("Failed to add LDAP user {0} to the SQL Table".format(ldapUser.mail))
|
|
cnx.close()
|
|
# Update seafile user profile with new name
|
|
updateSeafileUserName = request('admin/users/{0}/'.format(ldapUser.mail), seafileURL, seafileToken, "PUT", {"name": "{0}".format(ldapUser.displayName)})
|
|
if updateSeafileUserName['ok']:
|
|
logger.debug("User {0} name was updated to {1}".format(ldapUser.mail, ldapUser.displayName))
|
|
else:
|
|
logger.error("There was an error setting user {0} name to {1}".format(ldapUser.mail, ldapUser.displayName))
|
|
|
|
# Loop through the sql ldap users and disable those not in the ldap list
|
|
for sqlLDAPuser in sqlLDAPusers:
|
|
logger.debug("Searching for user {0} that has an email address, are enabled, and in the {1} group.".format(sqlLDAPuser[1], ldapFilter))
|
|
ldap.search(ldapBase, '(&(mail={0})(!(userAccountControl:1.2.840.113556.1.4.803:=2))({1}))'.format(sqlLDAPuser[1], ldapFilter), attributes=['*'])
|
|
logger.debug("Found {0} LDAP user.".format(len(ldap.entries)))
|
|
logger.debug("User: {0} - Email: {1} - UserDN: {2}".format(ldap.entries.name, ldap.entries.mail, ldap.entries.distinguishedName))
|
|
|
|
#if [item for item in ldapUsers if sqlLDAPuser[1] in item]:
|
|
#logger.debug("Found {0} in sql table".format(sqlLDAPuser[1]))
|
|
|
|
#seafileUsers = request('admin/search-user/?query=@johnhgaunt.com', seafileURL, seafileToken)['response']['user_list']
|
|
#for user in seafileUsers:
|
|
# logger.debug("User: {0} - Email: {1} - isActive: {2}".format(user['name'], user['email'], user['is_active']))
|
|
#print(ldap.entries[0].distinguishedName)
|
|
|
|
exit()
|
|
#Create a connection object, and bind with the given DN and password.
|
|
try:
|
|
conn = Connection(server, bindAccount, bindPassword, auto_bind=True)
|
|
print('LDAP Bind Successful.')
|
|
# Perform a search for a pre-defined criteria.
|
|
# Mention the search filter / filter type and attributes.
|
|
conn.search('CN=Users,dc=home,dc=johnhgaunt,dc=com', '(&(mail=*)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(memberOf=CN=Seafile,CN=Users,DC=home,DC=johnhgaunt,DC=com))')
|
|
# Print the resulting entries.
|
|
for entry in conn.entries:
|
|
print(entry)
|
|
except core.exceptions.LDAPBindError as e:
|
|
#If the LDAP bind failed for reasons such as authentication failure.
|
|
print('LDAP Bind Failed: ', e)
|
|
|
|
# sync ad users with seafile, if disabled or deleted ad user, disable in seafile
|
|
|
|
|
|
# get ad groups and import them into seafile
|
|
# loop through each group and list members
|
|
# compare members to users in seafile group
|
|
# add users to group if missing and in the seafile group
|
|
# remove members in not in group or seafile group
|
|
# remove seafile groups if ad group is removed
|
|
ldap.search(config['LDAP SERVER']['groupBaseDN'], '(objectClass=group)', attributes=['*'])
|
|
#print(ldap.entries)
|
|
for group in ldap.entries:
|
|
try:
|
|
if group.member:
|
|
logger.debug("{0}".format(group.name))
|
|
finally:
|
|
continue
|