Compare commits

..

No commits in common. "master" and "malberts/files_adminpy-1575907755669" have entirely different histories.

2 changed files with 6 additions and 166 deletions

View File

@ -18,35 +18,6 @@ Change free/busy to 36 months
If you want to make a change for all users pass the --all-users parameter. Example:
> python3 webapp_admin --all-users --icons Breeze
## Signatures
To restore, replace and backup signatures we need a two part, underscore separated filename consisting of a `name` and `id`.\
Example single user: `this-is-my-signature_1234.html`\
---
**Note**\
The hypens in the filename will be displayed as spaces in WebApp\
The username can also be part of the .html file, but is then ignored by the script.
In WebApp the ID is created based on the unix time, so the ID can be anything
---
Examples
Backup signature for user `henk`
> python3 webapp_admin -u henk --backup-signature
Restore signature for user `henk`
> python3 webapp_admin -u henk --restore-signature my-cool-signature_1615141312112.html
Replace signature for user `henk`
> python3 webapp_admin -u henk --replace-signature my-cool-signature_1615141312112.html
Restore signatures for all users
> python3 webapp_admin --all-users --restore-signature mycompany-signature_1412130992124.html
# Dependencies
- python3

View File

@ -18,12 +18,11 @@ try:
import OpenSSL.crypto
except ImportError:
pass
from datetime import datetime, timedelta
from datetime import datetime
from time import mktime
import getpass
import time
from optparse import OptionGroup
from tabulate import tabulate
try:
from dotty_dict import dotty
except ImportError:
@ -51,20 +50,11 @@ def opt_args(print_help=None):
group.add_option("--reset", dest="reset", action="store_true", help="Reset WebApp settings")
parser.add_option_group(group)
# Addionals stores group
group = OptionGroup(parser, "Store", "")
group.add_option("--add-store", dest="add_store", action="store", help="Add shared store")
group.add_option("--del-store", dest="del_store", action="store", help="Delete shared store")
group.add_option("--folder-type", dest="folder_type", action="store", help="Folder to add")
group.add_option("--subfolder", dest="sub_folder", action="store_true", help="Add subfolders")
group.add_option("--list-stores", dest="list_stores", action="store_true", help="List shared stores")
parser.add_option_group(group)
# Signature option group
group = OptionGroup(parser, "Signature", "")
group.add_option("--backup-signature", dest="backup_signature", action="store_true", help="Backup signature")
group.add_option("--restore-signature", dest="restore_signature", action="store", help="Restore signature (need file name)")
group.add_option("--replace-signature", dest="replace_signature", action="store", help="Replace existing signature, file layout must be: username_signature-name_signatureid.html or signature-name_signatureid.html ")
group.add_option("--replace", dest="replace", action="store_true", help="Replace existing signature, file layout must be: username_signature-name_signatureid.html")
group.add_option("--default-signature", dest="default_signature", action="store_true", help="Set signature as default one")
parser.add_option_group(group)
@ -78,7 +68,6 @@ def opt_args(print_help=None):
group = OptionGroup(parser, "S/MIME", "")
group.add_option("--export-smime", dest="export_smime", action="store_true", help="Export private S/MIME certificate")
group.add_option("--import-smime", dest="import_smime", action="store", help="Import private S/MIME certificate")
group.add_option("--remove-expired", dest="remove_expired", action="store_true", help="Remove expired public S/MIME certificates")
group.add_option("--public", dest="public_smime", action="store_true", help="Export/Import public S/MIME certificate")
group.add_option("--password", dest="password", action="store", help="set password")
group.add_option("--ask-password", dest="ask_password", action="store_true", help="ask for password if needed")
@ -209,73 +198,6 @@ def language(user, language):
write_settings(user, json.dumps(settings))
"""
Add shared store
"""
def add_store(user, user_to_add, folder_type, subfolder=False):
allowed_folder_types = ["all", "inbox", "calendar", "contact", "note", "task"]
if folder_type not in allowed_folder_types:
print("Unknown folder type allowed: {}".format(','.join(allowed_folder_types)))
sys.exit(1)
settings = read_settings(user)
if not settings['settings']['zarafa']['v1']['contexts'].get('hierarchy') or not settings['settings']['zarafa']['v1']['contexts']['hierarchy'].get('shared_stores'):
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'] = {}
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'][user_to_add]= {folder_type : {'folder_type': folder_type, 'show_subfolders': subfolder}}
print("Saving settings")
write_settings(user, json.dumps(settings))
"""
Delete shared store
"""
def del_store(user, user_to_del, folder_type=None):
if folder_type:
allowed_folder_types = ["all", "inbox", "calendar", "contact", "note", "task"]
if folder_type not in allowed_folder_types:
print("Unknown folder type allowed: {}".format(','.join(allowed_folder_types)))
sys.exit(1)
settings = read_settings(user)
if not settings['settings']['zarafa']['v1']['contexts'].get('hierarchy') or not settings['settings']['zarafa']['v1']['contexts']['hierarchy'].get('shared_stores'):
print("No additional stores found")
return
shared_store = settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'].get(user_to_del)
if not shared_store:
print("No additional stores found")
return
try:
if not folder_type:
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'].pop(user_to_del)
else:
settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores'][user_to_del].pop(folder_type)
except KeyError:
pass
print("Saving settings")
write_settings(user, json.dumps(settings))
"""
List all added stores
"""
def list_stores(user):
settings = read_settings(user)
try:
stores = settings['settings']['zarafa']['v1']['contexts']['hierarchy']['shared_stores']
except KeyError:
print("No additional stores found")
return
table_header = ["User", 'Folder type', 'Show subfolders']
table_data =[]
for user in stores:
for folder in stores[user]:
table_data.append([user, folder, stores[user][folder]['show_subfolders']])
print(tabulate(table_data, headers=table_header,tablefmt="grid"))
"""
Backup signature from the users store
@ -292,8 +214,6 @@ def backup_signature(user, location=None):
except Exception as e:
print('Could not load WebApp settings for user {} (Error: {})'.format(user.name, repr(e)))
sys.exit(1)
if not settings['settings']['zarafa']['v1']['contexts'].get("mail"):
settings['settings']['zarafa']['v1']['contexts']['mail'] = {}
if settings['settings']['zarafa']['v1']['contexts']['mail'].get('signatures'):
for item in settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all']:
name = settings['settings']['zarafa']['v1']['contexts']['mail']['signatures']['all'][item]['name']
@ -317,31 +237,16 @@ Restore signature into the users store
def restore_signature(user, filename, replace=None, default=None):
restorefile = filename
filename_split = filename.split('_')
if len(filename_split) == 2:
signaturename = filename_split[0].replace('-',' ')
if replace:
signatureid = filename_split[1].split('.')[0]
elif len(filename_split) == 3:
signaturename = filename_split[1].replace('-',' ')
if replace:
signatureid = filename_split[2].split('.')[0]
else:
if replace:
print('File format is not supported')
sys.exit(1)
with open(restorefile, 'r') as sigfile:
signaturehtml = sigfile.read()
if replace:
signatureid = filename.split('_')[2].split('.')[0]
action = 'Replacing'
else:
signaturename = filename.split('.')[0]
signatureid = int(time.time())
action = 'Adding'
signaturename = filename.split('_')[1].replace('-',' ')
signaturecontent = dict(
{u'name': signaturename, u'content': signaturehtml, u'isHTML': True})
settings = read_settings(user)
@ -443,7 +348,7 @@ def export_smime(user, location=None, public=None):
return
for cert in certificates:
if public and cert.prop(PR_MESSAGE_CLASS_W).value == 'WebApp.Security.Public':
if public and cert.prop(PR_MESSAGE_CLASS_w).value == 'WebApp.Security.Public':
extension = 'pub'
body = cert.text
else:
@ -523,27 +428,6 @@ def import_smime(user, cert_file, passwd, ask_password=None, public=None):
else:
print('Email address doesn\'t match')
"""
Remove expired S/MIME Public certificates
:param user: The user
"""
def remove_expired_smime(user):
# unable to loop over the associated items so getting the items in a list instead
certificates =list(user.store.root.associated.items())
if len(certificates) == 0:
print('No certificates found')
return
now = datetime.now()
for cert in certificates:
# We only want to remove the public certificate
if cert.prop(PR_MESSAGE_CLASS_W).value == 'WebApp.Security.Public':
if cert.prop(PR_MESSAGE_DELIVERY_TIME).value < now:
print('deleting public certificate {} ({})'.format(cert.subject, cert.prop(PR_MESSAGE_DELIVERY_TIME).value))
user.store.root.associated.delete(cert)
"""
Custom function to merge two dictionaries.
@ -553,7 +437,6 @@ but this function caused undesired behavior
:param dict1: The first dictionary
:param dict2: The second dictionary
"""
def mergedicts(dict1, dict2):
for k in set(dict1.keys()).union(dict2.keys()):
if k in dict1 and k in dict2:
@ -624,36 +507,22 @@ def main():
if options.language:
language(user, options.language)
if options.add_store:
add_store(user, options.add_store, options.folder_type, options.sub_folder)
if options.del_store:
del_store(user, options.del_store, options.folder_type)
if options.list_stores:
list_stores(user)
#Categories
if options.export_categories:
export_categories(user, options.file)
if options.import_categories:
import_categories(user, options.file)
# S/MIME import/export
if options.export_smime:
export_smime(user, options.location, options.public_smime)
if options.import_smime:
import_smime(user, options.import_smime, options.password, options.ask_password, options.public_smime)
if options.remove_expired:
remove_expired_smime(user)
# Signature
if options.backup_signature:
backup_signature(user, options.location)
if options.restore_signature:
restore_signature(user, options.restore_signature, False, options.default_signature)
if options.replace_signature:
restore_signature(user, options.replace_signature, True, options.default_signature)
restore_signature(user, options.restore_signature, options.replace, options.default_signature)
# Advanced injection option
if options.add_option: