Standardmäßig werden bei all-inkl.com PHP-Scripte per mod_php und nicht per fast-cgi ausgeführt. Wird innerhalb eines per mod_php ausgeführten Scriptes eine Dateien oder Verzeichnisse angelegt, so gehören diese Daten dem User und der Gruppe www-data. Dummerweise kann dann aber der FTP-User ggf. nicht mehr auf richtig auf die Dateien zugreifen, sodass die Dateien per SSH oder FTP nicht mehr abgeändert werden können.
Soll auf diese Dateien später per FTP zugegriffen werden (z.B. weil die Dateien gelöscht werden sollen), kann der Besitzer der Dateien im KAS-Frontend auf den FTP-User gesetzt werden. Die Änderungen werden dann in der Regel innerhalb der nächsten Minute umgesetzt und anschließend kann per FTP-Client oder per SSH gelöscht und beliebig geändert werden. Eine Änderung per chown (z.B. per SSH) ist leider nicht möglich. Das ganze Prozedere per KAS ist allerdings etwas umständlich; vor allem weil immer wieder zum Browser gewechselt muss, wenn eigentlich nur schnell eine Konfigurationsänderung per SSH durchgeführt werden soll.
Daher habe ich mir ein Python-Script geschrieben (change_owner.py), dass das Prozedere zumindest für meinen Workflow etwas vereinfacht.
#!/usr/bin/python # -*- coding: utf-8 -*- # # usage: change_owner.py [-h] [-d DIRECTORY] [-l LOGIN] [-o OWNER] [-p PASSWORD] # [-v] # # Den Owner eines Verzeichnisses mithilfe der KAS-Funktion 'Besitzrechte' # ändern. # # optional arguments: # -h, --help show this help message and exit # -d DIRECTORY, --directory DIRECTORY # Das Verzeichnis für das der Besitzer gewechstel # werden soll. # -l LOGIN, --login LOGIN # Der Domain-Name auf dem das Verzeichnis gewechselt # werden soll. Der Domain-NAme wird ohne subdomain # angegeben und ist gleichzeitig der Login-Name für das # KAS (Default: Wert wird vom Script erfragt). # -o OWNER, --owner OWNER # Der neue Besitzer des Verzeichnisses. # -p PASSWORD, --password PASSWORD # Passwort für den KAS-Zugang bei all-inkl (Default: # Passwort wird erfragt). # -v, --verbose Ausführliche Ausgaben aktivieren. # # Historie: # 2015.08.03: Erste Version erstellt # # (c) 2015 klein-gedruckt.de # # Python Imports import re, sys, os import requests import logging import getpass import argparse # Variablen definieren: BASE_URL = "https://kas.all-inkl.com/" act_path = "/" up_dir = {} # Warnmeldungen von modul Requests in separates Log umleiten # Anmerkung: Besser wäre Python >= 2.7.9 logging.captureWarnings(True) # Umlaute verfügbar machen um UnicodeDecodeError # zu behebem reload(sys) sys.setdefaultencoding('utf8') ############################################### # # Funktionen # def getDirs(dictDir): global act_path act_path = dictDir['dir'] payload = { 'abfrage': dictDir['dir'], 'login': dictDir['login'], 'auth': dictDir['auth'], 'server': dictDir['server'], 'feldname': 'verzeichnis', 'mode': 'dir', 'startdir': '', 'boxid': 'vorschlagsbox', 'autolisteid': 'auto_liste_vorschlaege' } r = requests.post(BASE_URL + '/inc/ajax_ftp_autopfad.php', data=payload) if not (r.status_code == requests.codes.ok): print '[*] Fehler beim Abruf der Tools-Seite.' exit(1) p = re.compile( r"^.*vorschlag_suchen\('([^']*)','([^']*)','([^']*)','([^']*)'.*$", re.MULTILINE ) matches = p.findall( r.text ) dirs = {} id = 1 for line in matches: if not (line[0] == '/./'): dirs.update( { id: { 'dir': line[0], 'login': line[1], 'auth': line[2], 'server': line[3] } }) id = id + 1 return dirs def showDirs(local_dirs): global act_path print print "[*] Verzeichnis auswählen:" print if not (act_path == "/"): print " [0] .." for id in local_dirs.keys(): print " [" + str(id) + "] " + local_dirs[id]['dir'] print print " [x] Aktuellen Pfad auswählen: " + act_path print choice = "" if not (act_path == "/"): ranger = [str(x) for x in range(0, len(local_dirs) + 1)] else: ranger = [str(x) for x in range(1, len(local_dirs) + 1)] choice = raw_input(" [##] wechselt in Verzeichnis, [x] wählt Verzeichnis aus: ") while (choice not in ranger) and not (choice == "x") and not (choice == 'X'): choice = raw_input(" [##] wechselt in Verzeichnis, [x] wählt Verzeichnis aus: ") if (choice == 'x') or (choice =='X'): return { 'path': act_path, 'dirs': '' } elif (choice == '0'): up_dir['dir'], tail = os.path.split(act_path) if (tail == ""): up_dir['dir'], tail = os.path.split(up_dir['dir']) return { 'path': '', 'dirs': getDirs(up_dir) } else: return { 'path': '', 'dirs': getDirs(local_dirs[int(choice)]) } # Parameter definieren und parsen parser = argparse.ArgumentParser(description="Den Owner eines Verzeichnisses mithilfe der KAS-Funktion 'Besitzrechte' ändern.") parser.add_argument('-d', '--directory', help='Das Verzeichnis für das der Besitzer gewechstel werden soll.') parser.add_argument('-l', '--login', help='Der Domain-Name auf dem das Verzeichnis gewechselt werden soll. Der Domain-NAme wird ohne subdomain angegeben und ist gleichzeitig der Login-Name für das KAS (Default: Wert wird vom Script erfragt).') parser.add_argument('-o', '--owner', help='Der neue Besitzer des Verzeichnisses.') parser.add_argument('-p', '--password', help='Passwort für den KAS-Zugang bei all-inkl (Default: Passwort wird erfragt).') parser.add_argument('-v', '--verbose', action="store_true", help='Ausführliche Ausgaben aktivieren.') args = parser.parse_args() if not args.login: args.login = raw_input('Domain und Username für KAS: ') if not args.password: args.password = getpass.getpass('Passwort für KAS: ') # 1. Schritt: Anmeldung am Portal payload = {'loginname': args.login, 'passwort': args.password, 'language': 'deutsch', 'sslcheckbox': 'on'} r = requests.post(BASE_URL, data=payload) if not (r.status_code == requests.codes.ok): print '[*] Fehler beim KAS-Login. Credentials korrekt?' exit(1) # 2. Schritt: Tools-Link aus der Antwort extrahieren p = re.compile( r'^.*href="(.*s=.*&a=[^"]*)\".*Tools.*$', re.MULTILINE ) m = p.search( r.text ) if m: if args.verbose: print '[*] Tools-Link extrahiert.' next_url = m.group(1) if args.verbose: print ' [*] Extrahierte URL: ' + next_url else: print '[*] Kein Tools-Link gefunden. Sind die Credentials korrekt?' exit(1) # 3. Schritt: Tools-Link aufrufen r = requests.get(BASE_URL + next_url) if not (r.status_code == requests.codes.ok): print '[*] Fehler beim Abruf der Tools-Seite.' exit(1) # 4. Schritt: Besitzrechte-Link extrahieren p = re.compile( r'^.*href="(.*s=.*&a=[^"]*)\".*Besitzrechte.*$', re.MULTILINE ) m = p.search( r.text ) if m: if args.verbose: print '[*] Besitzrechte-Link extrahiert.' next_url = m.group(1) if args.verbose: print ' [*] Extrahierte URL: ' + next_url else: print '[*] Kein Besitzrechte-Link gefunden.' exit(1) # 5. Schritt: Besitzrechte-Link abrufen formular = requests.get(BASE_URL + next_url) if not (formular.status_code == requests.codes.ok): print '[*] Fehler beim Abruf der Tools-Seite.' exit(1) # 6. Schritt: a extrahieren p = re.compile( r'^.*input.*style=.*name="a" value="([^"]*)".*$', re.MULTILINE ) m = p.search( formular.text ) if m: a = m.group(1) if args.verbose: print '[*] Wert für a extrahiert (a=' + a + ').' else: print '[*] Fehler beim Abruf der a-Tokens.' exit(1) # 7. Schritt: s extrahieren p = re.compile( r'^.*input.*style=.*name="s" value="([^"]*)".*$', re.MULTILINE ) m = p.search( formular.text ) if m: s = m.group(1) if args.verbose: print '[*] Wert für s extrahiert (s=' + s +').' else: print '[*] Fehler beim Abruf der s-Tokens.' exit(1) # 8. Schritt: Verzeichnis prüfen und auswählen if not args.directory: # / auslesen p = re.compile( r"^.*vorschlag_suchen\('([^']*)','([^']*)','([^']*)','([^']*)'.*$", re.MULTILINE ) matches = p.findall( formular.text ) if (matches[0][0]) and (matches[0][1]) and (matches[0][2]) and (matches[0][3]): up_dir = { 'dir': matches[0][0], 'login': matches[0][1], 'auth': matches[0][2], 'server': matches[0][3] } dirs = getDirs(up_dir) else: print '[*] Fehler beim Abruf der Tokens zum Abruf des Verzeichnis "/".' exit(1) path = '' while path == '' : buf = showDirs(dirs) path = buf['path'] dirs = buf['dirs'] else: print "[*] Ändere Verzeichnis " + args.directory + "." print " Hinweis: Es wird nicht geprüft ob das Verzeichnis existiert." # 9. Schritt: User extrahieren print p = re.compile( r'^.*option.*value="([^"]*)".*$', re.MULTILINE ) users = p.findall( formular.text ) if users: if (not args.owner) or ( not (args.owner in users)): if (args.owner) and (not (args.owner in users)): print '[*] Falscher Benutzername angegeben.' print '[*] Bitte gültigen Benutzer auswählen:' print i = 1 for user in users: print ' [',i,'] ' + user i = i + 1 print choice = "" ranger = [str(x) for x in range(1, len(users) + 1)] choice = raw_input(" Neuen Owner wählen (" + str(ranger) + "): ") while choice not in ranger: choice = raw_input(" Neuen Owner wählen (" + str(ranger) + "): ") choice = int(choice) - 1 args.owner = users[choice] else: print '[*] Fehler beim Abruf der User.' exit(1) # 10. Schritt: Berechtigungen setzen payload = {'user': args.owner, 'verzeichnis': path, 'rekursiv': 'rekursiv', 'a': a, 's': s, 'button1': 'Besitzrechte+jetzt+setzen'} r = requests.post(BASE_URL + "/index.php", data=payload) p = re.compile( r"^.*<p class='success'>Die Verzeichnisrechte.* Dies kann einige Minuten dauern\..*$", re.MULTILINE ) m = p.search( r.text ) if m: print print "[*] ERFOLG: Die Verzeichnisrechte werden zurück gesetzt. Es kann einige" print " Minuten dauern, bis die Änderungen wirksam werden." else: print "[*] Fehler beim Zurücksetzen der Verzeichnisrechte." exit (1)
Das Script kann z.B. auch auf dem all-inkl-Server abgelegt werden, so dass es direkt in einer SSH-Sitzung aufgerufen werden kann, wenn man es braucht. Dazu muss lediglich das Python-Modul auf dem Server eingerichtet werden. Wie das funktioniert ist dann Thema für einen eigenen Blog-Beitrag.
Hallo Stephan,
ich habe eine ähnliches Skript programmiert. Ich verwende dabei allerdings die KAS-API, so dass das Parsen der HTML-Seiten entfällt. Das Skript und weitere Tools für den SSH-Zugang von all-inkl.com ist bei Github abgelegt: https://github.com/e-dschungel/kas_tools
Hallo,
damals kannte ich die API noch nicht. Mittlerweile würde ich es auch über die API lösen.