parent
b71ae26a44
commit
3f0e8e0a24
|
|
@ -0,0 +1,178 @@
|
|||
Abend
|
||||
Abendrot
|
||||
Aldebaran
|
||||
Allianz
|
||||
Analyse
|
||||
Angebot
|
||||
Antwort
|
||||
Aufgabe
|
||||
Auftakt
|
||||
Augenstern
|
||||
Augenweide
|
||||
Ausgleich
|
||||
Bahnhof
|
||||
Basis
|
||||
Bedeutung
|
||||
Begegnung
|
||||
Beispiel
|
||||
Beitrag
|
||||
Beratung
|
||||
Berufung
|
||||
Bewegung
|
||||
Blickfang
|
||||
Butterblume
|
||||
Borgelicht
|
||||
Chance
|
||||
Charakter
|
||||
Chiffon
|
||||
Denken
|
||||
Dialog
|
||||
Dutzend
|
||||
Ebene
|
||||
Einheit
|
||||
Einigung
|
||||
Energie
|
||||
Farbe
|
||||
Farbenfroh
|
||||
Fernweh
|
||||
Feuervogel
|
||||
Firlefanz
|
||||
Firmament
|
||||
Forschung
|
||||
Freiheit
|
||||
Freude
|
||||
Frieden
|
||||
Fusion
|
||||
Galerie
|
||||
Garten
|
||||
Gedanke
|
||||
Gedanken
|
||||
Gegenwart
|
||||
Geist
|
||||
Gestalt
|
||||
Haltung
|
||||
Heimat
|
||||
Heimelig
|
||||
Herbst
|
||||
Herzlich
|
||||
Himmel
|
||||
Himmelsblau
|
||||
Hoffnung
|
||||
Insel
|
||||
Interesse
|
||||
Jahrzehnt
|
||||
Jenseits
|
||||
Joghurt
|
||||
Jubel
|
||||
Kamera
|
||||
Karte
|
||||
Kenntnis
|
||||
Kleinod
|
||||
Konzert
|
||||
Kraft
|
||||
Kultur
|
||||
Kunst
|
||||
Labsal
|
||||
Lebenslust
|
||||
Lehre
|
||||
Libelle
|
||||
Licht
|
||||
Lichtblick
|
||||
Lichtspiel
|
||||
Liebe
|
||||
Literatur
|
||||
Lobhudeln
|
||||
Luftikus
|
||||
Luftschlange
|
||||
Luftschloss
|
||||
Medium
|
||||
Meister
|
||||
Mondschein
|
||||
Morgenstern
|
||||
Morgentau
|
||||
Mummenschanz
|
||||
Museum
|
||||
Nachbar
|
||||
Nacht
|
||||
Naschkatze
|
||||
Naseweis
|
||||
Natur
|
||||
Nordstern
|
||||
Ofenwarm
|
||||
Ohrenschmaus
|
||||
Ohrwurm
|
||||
Ordnung
|
||||
Ostwind
|
||||
Pflege
|
||||
Plauderei
|
||||
Prinzip
|
||||
Purzelbaum
|
||||
Pusteblume
|
||||
Pustekuchen
|
||||
Quasar
|
||||
Quelle
|
||||
Quitte
|
||||
Rebell
|
||||
Reform
|
||||
Regenbogen
|
||||
Richtung
|
||||
Runde
|
||||
Sammlung
|
||||
Samtpfote
|
||||
Sandkasten
|
||||
Sandstein
|
||||
Saumselig
|
||||
Schatten
|
||||
Schlummern
|
||||
Schmollmund
|
||||
Schutz
|
||||
Seele
|
||||
Sektlaune
|
||||
Sommer
|
||||
Sommerfrische
|
||||
Sonne
|
||||
Sperenzchen
|
||||
Spiegel
|
||||
Spielen
|
||||
Sprache
|
||||
Steckenpferd
|
||||
Sternenzelt
|
||||
Sternschnuppe
|
||||
Sternstunde
|
||||
Stimmung
|
||||
Stubentiger
|
||||
Szene
|
||||
Tagtraum
|
||||
Teilhabe
|
||||
Tendenz
|
||||
Theater
|
||||
Tollpatsch
|
||||
Tradition
|
||||
Traum
|
||||
Ukulele
|
||||
Umwelt
|
||||
Unsinn
|
||||
Variante
|
||||
Vertrauen
|
||||
Vollmond
|
||||
Vorfreude
|
||||
Vorschlag
|
||||
Waffel
|
||||
Wahrheit
|
||||
Wanderlust
|
||||
Wildwasser
|
||||
Wintertag
|
||||
Wissen
|
||||
Wundervoll
|
||||
Wunschtraum
|
||||
Xenon
|
||||
Xenophil
|
||||
Xylophon
|
||||
Yacht
|
||||
Yttrium
|
||||
Yucca
|
||||
Zeichen
|
||||
Zeitlos
|
||||
Zeitpunkt
|
||||
Zeitraum
|
||||
Zwielicht
|
||||
|
|
@ -1,38 +1,62 @@
|
|||
# Generate import file
|
||||
# Generate Userlist
|
||||
#
|
||||
# Rev 1.1, 10/22/22
|
||||
# - changed the -y (year of enrolement) parameter to a more general -s (suffix)
|
||||
# - updated the usage message to include that change
|
||||
# - added a lot of comments, especially regarding the default values
|
||||
# - removed the unused fileinput import
|
||||
# - changed the defaults to fit more common scenarios
|
||||
# Rev 1.0, 10/20/22
|
||||
# - a simple script to generate CSV-files suitable as input
|
||||
# for the importUsers script.
|
||||
# - this script is rather specifically designed to accomodate
|
||||
# the needs of our school's user naming scheme. Different
|
||||
# naming schemes should be easy to implement though.
|
||||
# - the script generates default passwords by picking three
|
||||
# words from a file called WordList.txt living in the same
|
||||
# directory as the script file.
|
||||
# Options
|
||||
# -d,--domain: domain name for the email address, this is required
|
||||
# -g,--groups: comma-separated list of groups added to every user
|
||||
# -s,--suffix: added to every username and the resulting filename
|
||||
# in this release, the suffix is added to the last group name as well
|
||||
# -f,--file: file of usernames, first and last name separated by comma
|
||||
|
||||
import sys, getopt
|
||||
import csv
|
||||
import os,io
|
||||
import fileinput
|
||||
import random
|
||||
|
||||
def main(argv):
|
||||
wordList = open("WordList.txt").read().splitlines()
|
||||
groups = "Schueler"
|
||||
yoe = "00"
|
||||
domainname="hd.waldorf.one"
|
||||
# add some default values for the options
|
||||
groups = "Users" # add default groups for all imports here
|
||||
suffix = ""
|
||||
domainname="example.com"
|
||||
filename = "names.csv"
|
||||
# next is a list of characters common in users actual names but unsuitable for login names
|
||||
# for this script, those are specific to german names. Spaces get replaced with dashes.
|
||||
char_map = {ord("ä"):"ae", ord("ü"):"ue", ord("ö"):"oe", ord("ß"):"ss", ord(" "):"-"}
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(argv,"hg:d:y:f:",["help","groups=","domain=","year=","file="])
|
||||
opts, args = getopt.getopt(argv,"hg:d:s:f:",["help","groups=","domain=","suffix=","file="])
|
||||
except getopt.GetoptError:
|
||||
print("Usage: importUsers.py -d <domainname> [-g <groups>] [-y <year of enrollment]> -f <datafile>")
|
||||
print("Usage: importUsers.py -d <domainname> [-g <groups>] [-s <common suffix for this import]> -f <datafile>")
|
||||
sys.exit(2)
|
||||
for opt, arg in opts:
|
||||
if opt == '-h':
|
||||
print("Usage: importUsers.py -d <domainname> [-g <groups>] -y <year of enrollment> -f <datafile> ")
|
||||
print("Usage: importUsers.py -d <domainname> [-g <groups>] -s <common suffix for this import> -f <datafile> ")
|
||||
elif opt in ('-d', '--domain'):
|
||||
domainname = arg
|
||||
elif opt in ('-g', '--groups'):
|
||||
groups = groups + "," + arg
|
||||
groups = groups + "," + arg # as per importUsers specs, the groups are comma-separated
|
||||
elif opt in ('-f', '--file'):
|
||||
filename = arg
|
||||
elif opt in ('-y', '--year'):
|
||||
yoe = arg
|
||||
groups = groups + ",schueler-hd-" + yoe
|
||||
elif opt in ('-s', '--suffix'):
|
||||
suffix = arg
|
||||
groups = groups + suffix
|
||||
|
||||
outputFileName = "users_" + yoe + ".csv"
|
||||
char_map = {ord("ä"):"ae", ord("ü"):"ue", ord("ö"):"oe", ord("ß"):"ss", ord(" "):"-"}
|
||||
outputFileName = "users" + suffix + ".csv"
|
||||
with open(outputFileName,"w+",newline='') as outputFile:
|
||||
header = ["first_name","last_name","email_address","sis_username","user_groups","password"]
|
||||
writer = csv.DictWriter(outputFile,fieldnames=header,delimiter=";", quoting=csv.QUOTE_MINIMAL)
|
||||
|
|
@ -40,9 +64,9 @@ def main(argv):
|
|||
users = open(filename,"r").read().splitlines()
|
||||
for user in users:
|
||||
first_name,last_name = tuple(user.split(","))
|
||||
username = first_name.casefold().translate(char_map) + "." + last_name.casefold().translate(char_map) + "-" + yoe
|
||||
username = first_name.casefold().translate(char_map) + "." + last_name.casefold().translate(char_map) + suffix
|
||||
address = username + "@" + domainname
|
||||
password = "-".join(random.sample(wordList,3))
|
||||
password = "-".join(random.sample(wordList,3)) # pick three words at ramdom, join them with a dash
|
||||
writer.writerow({"first_name":first_name,"last_name":last_name,"email_address":address,"sis_username":username,"user_groups":groups,"password":password})
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
# Simple Cloudron bulk user import script
|
||||
# Uses the Cloudron REST API v1
|
||||
# Rev 1.4, 10/19/22, changes:
|
||||
# - added local storage of access token including a limited means to check for validity
|
||||
# - if mailboxes were supposed to be added and that failed, the tool will dump the missing users
|
||||
# into a csv file formatted to import into the Cloudron Email management section.
|
||||
# Rev 1.3, 10/16/22, changes:
|
||||
# - added option to add passwords as part of the import
|
||||
# Please note that this method obviously discloses passwords to the admin, so
|
||||
|
|
@ -27,9 +31,7 @@
|
|||
# Douglas;Adams;douglas.adams@example.com;d.adams;staff,writers
|
||||
# ToDo:
|
||||
# - groups from the user_group field must already exist. If a specified group is not available, an error is displayed.
|
||||
# Future revisions could ask whether missing groups should be created
|
||||
# - currently, a new access token is generated for every run of the script.
|
||||
# This could be stored somewhere for consecutive runs, the standard expiry is a year
|
||||
# Future revisions could ask whether missing groups should be created.
|
||||
# - could use some sanity checks for input (low priority, if you want to sabotage yourself, go ahead)
|
||||
# - totpTokens are always requested, even if the user hasn't configured token 2FA (they probably should)
|
||||
# - Check, if the supplied email address is in the same domain as the Cloudron if the -m option is set
|
||||
|
|
@ -38,9 +40,23 @@ import sys, getopt
|
|||
import getpass
|
||||
import requests
|
||||
import csv
|
||||
import os
|
||||
import pickle
|
||||
from json import loads
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
def requestAccessToken(domain, username=''):
|
||||
# check for valid access token
|
||||
if os.path.exists(".cat.pkl"):
|
||||
with open(".cat.pkl","rb") as cloudronAccessTokenFile:
|
||||
accessTokenDict = pickle.load(cloudronAccessTokenFile)
|
||||
|
||||
terminationDate = datetime.now() + relativedelta(years=1)
|
||||
if (accessTokenDict['domain'] == domain) and (accessTokenDict['date'] < terminationDate):
|
||||
return accessTokenDict['token']
|
||||
|
||||
# current token is either invalid or for another domain, create a new one
|
||||
apiBasePath = 'https://' + domain + "/api/v1"
|
||||
if( username == '' ):
|
||||
username = input("Enter username for " + domain + ": ")
|
||||
|
|
@ -49,7 +65,12 @@ def requestAccessToken(domain, username=''):
|
|||
totpToken = input("Enter current totpToken: ")
|
||||
a = requests.post(apiBasePath + '/cloudron/login', json={"username":username, "password":password, "totpToken":totpToken})
|
||||
if( a.status_code == requests.codes.ok ):
|
||||
return a.json()['accessToken']
|
||||
accessToken = a.json()['accessToken']
|
||||
accessTokenDict = {"domain":domain,"date":datetime.now(),"token":accessToken}
|
||||
with open(".cat.pkl","wb+") as cloudronAccessTokenFile:
|
||||
pickle.dump(accessTokenDict,cloudronAccessTokenFile)
|
||||
|
||||
return accessToken
|
||||
else:
|
||||
print(f"Error requesting access token: {a.status_code}")
|
||||
sys.exit(2)
|
||||
|
|
@ -72,6 +93,7 @@ def main(argv):
|
|||
sys.exit()
|
||||
elif opt in ('-f', '--file'):
|
||||
dataFilePath = arg
|
||||
directory,dataFileName = os.path.split(dataFilePath)
|
||||
elif opt in ('-d', '--domain'):
|
||||
domain = arg
|
||||
elif opt in ('-u','--username'):
|
||||
|
|
@ -135,6 +157,7 @@ def main(argv):
|
|||
|
||||
# If a mailbox was to be added, do that now (we have the id already)
|
||||
if( addmailbox == True ):
|
||||
missingMailboxesFilename = "mailbox_" + dataFileName
|
||||
payload = {"name":entry['sis_username'], "ownerId":curUserId, "ownerType":"user"}
|
||||
userDomain = entry['email_address'].split('@')[-1]
|
||||
mailUrl = apiBasePath + '/mail/'+userDomain+'/mailboxes?access_token='+accessToken
|
||||
|
|
@ -143,6 +166,11 @@ def main(argv):
|
|||
print(f"Mailbox for user {displayName} created as {entry['email_address']}")
|
||||
else:
|
||||
print(f"Could not create mailbox for user {displayName}, error code {p.status_code}")
|
||||
print(f"Dumping data to import file <mailbox_{dataFilePath}>")
|
||||
|
||||
with open(missingMailboxesFilename,"a+",newline="") as mailboxFile:
|
||||
writer = csv.writer(mailboxFile,delimiter=",")
|
||||
writer.writerow([entry['sis_username'],userDomain,curUserId,"user"])
|
||||
|
||||
else:
|
||||
print(f"User {displayName} could not be created, statuscode {r.status_code}")
|
||||
|
|
|
|||
Loading…
Reference in New Issue