123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- #!/usr/bin/env python
- import argparse
- import json
- import sys
- import time
- try:
- import pwd
- import spwd
- except ImportError:
- pwd = None
- class ConvertUsers:
- def __init__(self, input_file, users_format, output_file, min_uid, max_uid, usernames, force_uid, force_gid):
- self.input_file = input_file
- self.users_format = users_format
- self.output_file = output_file
- self.min_uid = min_uid
- self.max_uid = max_uid
- self.usernames = usernames
- self.force_uid = force_uid
- self.force_gid = force_gid
- self.SFTPGoUsers = []
- def buildUserObject(self, username, password, home_dir, uid, gid, max_sessions, quota_size, quota_files, upload_bandwidth,
- download_bandwidth, status, expiration_date, allowed_ip=[], denied_ip=[]):
- return {'id':0, 'username':username, 'password':password, 'home_dir':home_dir, 'uid':uid, 'gid':gid,
- 'max_sessions':max_sessions, 'quota_size':quota_size, 'quota_files':quota_files, 'permissions':{'/':["*"]},
- 'upload_bandwidth':upload_bandwidth, 'download_bandwidth':download_bandwidth,
- 'status':status, 'expiration_date':expiration_date,
- 'filters':{'allowed_ip':allowed_ip, 'denied_ip':denied_ip}}
- def addUser(self, user):
- user['id'] = len(self.SFTPGoUsers) + 1
- print('')
- print('New user imported: {}'.format(user))
- print('')
- self.SFTPGoUsers.append(user)
- def saveUsers(self):
- if self.SFTPGoUsers:
- data = {'users':self.SFTPGoUsers}
- jsonData = json.dumps(data)
- with open(self.output_file, 'w') as f:
- f.write(jsonData)
- print()
- print('Number of users saved to "{}": {}. You can import them using loaddata'.format(self.output_file,
- len(self.SFTPGoUsers)))
- print()
- sys.exit(0)
- else:
- print('No user imported')
- sys.exit(1)
- def convert(self):
- if self.users_format == 'unix-passwd':
- self.convertFromUnixPasswd()
- elif self.users_format == 'pure-ftpd':
- self.convertFromPureFTPD()
- else:
- self.convertFromProFTPD()
- self.saveUsers()
- def isUserValid(self, username, uid):
- if self.usernames and not username in self.usernames:
- return False
- if self.min_uid >= 0 and uid < self.min_uid:
- return False
- if self.max_uid >= 0 and uid > self.max_uid:
- return False
- return True
- def convertFromUnixPasswd(self):
- days_from_epoch_time = time.time() / 86400
- for user in pwd.getpwall():
- username = user.pw_name
- password = user.pw_passwd
- uid = user.pw_uid
- gid = user.pw_gid
- home_dir = user.pw_dir
- status = 1
- expiration_date = 0
- if not self.isUserValid(username, uid):
- continue
- if self.force_uid >= 0:
- uid = self.force_uid
- if self.force_gid >= 0:
- gid = self.force_gid
- # FIXME: if the passwords aren't in /etc/shadow they are probably DES encrypted and we don't support them
- if password == 'x' or password == '*':
- user_info = spwd.getspnam(username)
- password = user_info.sp_pwdp
- if not password or password == '!!' or password == '!*':
- print('cannot import user "{}" without a password'.format(username))
- continue
- if user_info.sp_inact > 0:
- last_pwd_change_diff = days_from_epoch_time - user_info.sp_lstchg
- if last_pwd_change_diff > user_info.sp_inact:
- status = 0
- if user_info.sp_expire > 0:
- expiration_date = user_info.sp_expire * 86400
- self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, 0, 0, 0, 0, 0, status,
- expiration_date))
- def convertFromProFTPD(self):
- with open(self.input_file, 'r') as f:
- for line in f:
- fields = line.split(':')
- if len(fields) > 6:
- username = fields[0]
- password = fields[1]
- uid = int(fields[2])
- gid = int(fields[3])
- home_dir = fields[5]
- if not self.isUserValid(username, uid):
- continue
- if self.force_uid >= 0:
- uid = self.force_uid
- if self.force_gid >= 0:
- gid = self.force_gid
- self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, 0, 0, 0, 0, 0, 1, 0))
- def convertPureFTPDIP(self, fields):
- result = []
- if not fields:
- return result
- for v in fields.split(','):
- ip_mask = v.strip()
- if not ip_mask:
- continue
- if ip_mask.count('.') < 3 and ip_mask.count(':') < 3:
- print('cannot import pure-ftpd IP: {}'.format(ip_mask))
- continue
- if '/' not in ip_mask:
- ip_mask += '/32'
- result.append(ip_mask)
- return result
- def convertFromPureFTPD(self):
- with open(self.input_file, 'r') as f:
- for line in f:
- fields = line.split(':')
- if len(fields) > 16:
- username = fields[0]
- password = fields[1]
- uid = int(fields[2])
- gid = int(fields[3])
- home_dir = fields[5]
- upload_bandwidth = 0
- if fields[6]:
- upload_bandwidth = int(int(fields[6]) / 1024)
- download_bandwidth = 0
- if fields[7]:
- download_bandwidth = int(int(fields[7]) / 1024)
- max_sessions = 0
- if fields[10]:
- max_sessions = int(fields[10])
- quota_files = 0
- if fields[11]:
- quota_files = int(fields[11])
- quota_size = 0
- if fields[12]:
- quota_size = int(fields[12])
- allowed_ip = self.convertPureFTPDIP(fields[15])
- denied_ip = self.convertPureFTPDIP(fields[16])
- if not self.isUserValid(username, uid):
- continue
- if self.force_uid >= 0:
- uid = self.force_uid
- if self.force_gid >= 0:
- gid = self.force_gid
- self.addUser(self.buildUserObject(username, password, home_dir, uid, gid, max_sessions, quota_size,
- quota_files, upload_bandwidth, download_bandwidth, 1, 0, allowed_ip,
- denied_ip))
- if __name__ == '__main__':
- parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter, description=
- 'Convert users to a JSON format suitable to use with loadddata')
- supportedUsersFormats = []
- help_text = ''
- if pwd is not None:
- supportedUsersFormats.append('unix-passwd')
- help_text = 'To import from unix-passwd format you need the permission to read /etc/shadow that is typically granted to the root user only'
- supportedUsersFormats.append('pure-ftpd')
- supportedUsersFormats.append('proftpd')
- parser.add_argument('input_file', type=str)
- parser.add_argument('users_format', type=str, choices=supportedUsersFormats, help=help_text)
- parser.add_argument('output_file', type=str)
- parser.add_argument('--min-uid', type=int, default=-1, help='if >= 0 only import users with UID greater or equal ' +
- 'to this value. Default: %(default)s')
- parser.add_argument('--max-uid', type=int, default=-1, help='if >= 0 only import users with UID lesser or equal ' +
- 'to this value. Default: %(default)s')
- parser.add_argument('--usernames', type=str, nargs='+', default=[], help='Only import users with these usernames. ' +
- 'Default: %(default)s')
- parser.add_argument('--force-uid', type=int, default=-1, help='if >= 0 the imported users will have this UID in ' +
- 'SFTPGo. Default: %(default)s')
- parser.add_argument('--force-gid', type=int, default=-1, help='if >= 0 the imported users will have this GID in ' +
- 'SFTPGo. Default: %(default)s')
- args = parser.parse_args()
- convertUsers = ConvertUsers(args.input_file, args.users_format, args.output_file, args.min_uid, args.max_uid,
- args.usernames, args.force_uid, args.force_gid)
- convertUsers.convert()
|