Wie kann ich einen unendlich lang gültigen Facebook Page Accesstoken erzeugen?

Facebook Access Tokens sind eine ziemlich fiese Sache, wenn man Server only Anwendungen bauen möchte – also keine wirkliche Chance hat, den User einen Token besorgen zu lassen. Zusätzlich haben die “normalen” Access Tokens bei Facebook das Problem, dass sie spätestens nach 60 Tagen ungültig sind. Es gibt aber derzeit noch eine Möglichkeit, an unendlich gültige Access Tokens zu kommen. Mit diesen Tokens könnt ihr beliebig auf euer Seite posten, Statistiken abfragen usw. – und an diesen Token kommt ihr so:

  1. Zunächst müsst ihr Admin der gewünschten Fan Page sein
  2. erstellt eine Facebook App – natürlich mit dem gleichen User, der auch Admin der Seite ist.
  3. kopiert in den Einstellungen der App die App-ID sowie das App Secret
  4. öffnet den Facebook Graph API Explorer
  5. oben rechts ist ein Dropdown, in dem ihr die eben erstellte App auswählt (anfänglich steht da “Graph API Explorer” drin)
  6. nun klickt ihr auf das “Get Token”-Dropdown und wählt da “Get User Access Token” – dabei ist es wichtig, dass ihr in der nun erscheinenden Übersicht das Häkchen bei “manage_pages” setzt
  7. kopiert nun den kurzfristigen Token aus dem Textfeld in der Mitte und ruft folgende URL auf:
    https://graph.facebook.com/oauth/access_token?client_id=[APP_ID]&client_secret=[APP_SECRET]&grant_type=fb_exchange_token&fb_exchange_token=[TOKEN]
  8. kopiert euch den nun angezeigten langfristigen Token (LONG_LIVING_TOKEN, 60 Tage gültig)
  9. ruft nun die folgende URL auf:

    https://graph.facebook.com/me/accounts?access_token=[LONG_LIVING_TOKEN]

  10. in dem nun erscheinenden JSON seht ihr alle von euch verwalteten Seiten sowie deren unendlich lang gültigen Tokens für die verwendete App

Zur Überprüfung ruft ihr einfach das Access Token Debug Tool auf: 

https://developers.facebook.com/tools/debug/accesstoken

Hier könnt ihr den Token eintragen und bekommt dann Informationen darüber – und eben auch die Gültigkeit.

Wie lade ich in Django 1.10 ein Bild von der Festplatte per Django Command hoch?

Für den Fall, dass man ein Bild in eine Django Applikation uploaden möchte, gibt es unzählige Tutorials – kniffelig wird es dann ein bisschen, wenn man ein Bild von der Festplatte des Servers, auf dem die Applikation läuft, mittels Django Command hochladen möchte. In meinem Beispiel importiere ich Bilder per Cronjob aus einem anderen Programm. Bisher ging der Upload auch ohne Probleme, aber seit Django 1.9 oder 1.10 wurde das Sicherheitskonzept geändert – man darf keine Dateien mehr ausserhalb des Media_Path Kontextes hochladen (Suspicious File Operation. Um das Problem zu umgehen, muss man das “normale” Fileobject, welches man normalerweise dann an das Model übergeben würde, in den Django File Wrapper packen. Dazu importiert ihr diesen Wrapper wie im Beispiel unten, übergebt den realen Pfad zum Bild und, und das ist nun wichtig, übergebt per “name” Parameter einen beliebigen Dateinamen, mit dem diese Datei dann im “Upload Prozess” von Django ankommt. Nun müsst ihr das “image” Objekt einfach nur an den Image Parameter eures Models hängen, und schon klappt das Einspielen der Datei wieder. Bei mir war es tatsächlich der fehlende “name” Parameter, der mich in die Verzweiflung getrieben hat 🙂

from django.core.files import File
image = File(open("[PATH_TO_REAL_FILE]", "rb"), name="[SOME_FILE_NAME]")

[Quicktip] Nginx Reverse Proxy mit Basic Auth

Nehmen wir an, ihr richtet einen Nginx als reverse Proxy ein und möchtet nun, dass bestimmte Subdomains, die von anderen Servern durchgeschliffen werden, per Basic Auth “geschützt” werden sollen. Dann werdet ihr wahrscheinlich auf das Problem stoßen, dass ihr immer wieder nach dem Basic Auth Login gefragt werdet und der Reverse Proxy nicht korrekt agiert. Die Lösung ist ganz einfach: ihr müsst verhindern, dass die Basic Auth Header weitergereicht werden. Und das geht so:

server {
    listen 80;
    server_name foo.bar.com;
    access_log            /var/log/nginx/foo.access.log;
    location / {
      proxy_set_header        Authorization "";    # <== das ist die wichtige Zeile, die verhindert, dass Basic Auth weiter gereicht wird!
      auth_basic              "Protected";
      auth_basic_user_file    /etc/nginx/basic_auth;
      proxy_set_header        X-Real-IP $remote_addr;
      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header        X-Forwarded-Proto $scheme;
      proxy_set_header        Host [YOUR_REMOTE_DOMAIN];
      proxy_set_header        Accept-Encoding "";
      proxy_pass              [YOUR_REMOTE_URL];
      proxy_read_timeout      90;
    }
}

[Quicktip] Wie kann ich alle installierten Python pip Packages upgraden?

Über einen kleinen Umweg kann man auf der Bash ganz einfach alle aktuell installierten pip Pakete updaten / upgraden:

pip freeze | sed -e 's/==.*//g' > upgrade.txt
pip install --upgrade -r upgrade.txt
rm upgrade.txt

Das Script lässt sich alle installierten Pakete ausgeben, entfernt das “==[Versionsnummer]” hinter dem Namen und packt diese Liste in die Datei upgrade.txt. Anschließend wird diese Datei bei einem “pip upgrade” als “requirements.txt” übergeben und mit dem upgrade Befehl ausgeführt.

Jenkins OSX Slave für das automatisierte Bauen von iOS Apps

Die Aufgabe ist simpel: ich möchte in meine Jenkins Build Umgebung einen Rechner mit Mac OSX einbinden, der für mich automatisiert iOS Apps bauen soll. Für die Vereinfachung verwende ich dazu das xctool von Facebook, welches das Handling von XCode und co. übernimmt.

Mein Vorgehen war folgendes: Jenkins User auf dem Zielsystem eingerichtet und anschließend den Jenkins Slave per ssh vom Master aus gestartet. Das funktioniert auch alles wunderbar, aber sobald man im Build versucht auf eine Gui Applikation zuzugreifen – das macht z.B. der Aufruf der Unit Tests, weil diese den iPhone Simulator benötigen – knallt xctool ohne eine Fehlermeldung mit einem Exit Code 1 weg. Das Problem an der Stelle ist einfach, dass es in OSX einen Unterschied macht, ob man ein Programm per ssh oder direkt in einer Shell auf dem System startet. Die Variante über ssh darf eben nicht auf GUI Applikationen zugreifen. Und somit laufen die Tests nicht.

Korrekterweise muss man den Jenkins Slave als per JNLP starten. Dazu stellt man im Master in den Slave Einstellungen die „Startmethode“ auf „Starte Slave Agenten über JNLP“. Nach dem Speichern wir einem in der Übersicht des Knotens ein Shell Command angezeigt, den man auf dem Slave ausführen soll:

java -jar slave.jar -jnlpUrl http://[PFAD_ZUM_JENKINS_MASTER]/computer/[SLAVE_NAME]/slave-agent.jnlp -secret [SECRET_HASH]

In diesem Command ist die Datei „slave.jar“ verlinkt und kann heruntergeladen werden. Genau das sollte man auf dem OSX Slave machen und die Datei irgendwo auf der Platte ablegen. Anschließen startet man das Programm „Automator“. Dort wählt man „Applikation“ aus und sucht anschließend in der Bibliothek nach „Shell Script ausführen“. Diesen Eintrag zieht man rechts in die graue Fläche.

In das nun vorhandene Textfeld trägt man folgendes Shellscript ein:

cd [PFAD_ZUR_JAR_DATEI]
java -jar slave.jar -jnlpUrl http://[PFAD_ZUM_JENKINS_MASTER]/computer/[SLAVE_NAME]/slave-agent.jnlp -secret [SECRET_HASH]

Das ganze speichert ihr im Ordner Programme als „run_jenkins_slave“ ab. Nun geht ihr in die „Systemeinstellungen” -> “Benutzer und Gruppen“ und wählt bei Jenkins die „Startobjekte“ aus. Dort fügt ihr nun das eben erstellte Programm „run_jenkins_slave“ hinzu. Anschließend geht ihr noch auf „Anmeldeoptionen“ und stellt da den User „Jenkins“ für den automatischen Login ein.

Wenn ihr nun den OSX Rechner neu startet, dann sollte er automatisch einen Jenkins Slave starten und diesen beim Master registrieren. Nun sind wir genauso weit wie mit dem ssh Weg – aber: wenn man nun Jobs startet, dann dürfen diese auch auf GUI Applikationen zugreifen. Sprich, die Unit Tests mit dem iPhone Simulator laufen nun ohne Probleme durch. Der Jenkins Slave verhält sich in etwa so, als ob man direkt auf dem Desktop eine Shell startet und dann Befehle ausführt.

[Quicktip] Jenkins meckert mit einem reject HostKey trotz korrektem Eintrag in der known_hosts Datei

Ihr möchtet mit Jenkins auf einen anderen Server per ssh/scp zugreifen, habt auf der Shell bereits erfolgreich mit dem User Jenkins eine ssh Verbindung aufbauen können, aber im Build Prozess bekommt ihr folgenden Fehler:

com.jcraft.jsch.JSchException: reject HostKey: ...

Das Problem ist relativ simpel zu lösen – die Einträge in der ~/.ssh/known_hosts dürfen nicht verschlüsselt sein. Lösen könnt ihr das folgendermaßen:
– alten Hosts Eintrag löschen: ssh-keygen -R [SERVER_NAME]
– in eurer ssh_config den Parameter „HashKnownHosts“ auf “no” setzen
– per ssh auf den Server eine Verbindung aufbauen und die Frage, ob der Key hinzugefügt werden darf, mit ja beantworten

Nun sollte der Eintrag im Klartext in der known_hosts stehen und Jenkins sollte die Verbindung aufbauen können.

[Quicktip] Jenkins baut bereits gelöschte git Branches

Besonders wenn man einen Jenkins Job nicht auf einen bestimmten Branch einschränkt wird man dieses Problem schnell bemerken: Jenkins baut in bestimmten Fällen auf Basis von git Branches, die eigentlich bereits gelöscht sind. Der Fehler tritt auf, weil Jenkins per default das lokale git Repository nicht mit dem Origin bezüglich gelöschter Branches synchronisiert. 

Dieses Problem kann man relativ einfach beheben. Dazu geht ihr in die Konfiguration des betroffenen Jenkins Job und im Bereich “Source-Code-Management“ wählt ihr im Dropdown “Additional Behaviours“ den Punkt “Prune stale remote-tracking branches“. Damit wird vor dem Pull bzw. Fetch genau dieser Sync durchgeführt. Sprich, es werden lokal alle Branches gelöscht, die im Origin nicht mehr vorhanden sind.

Wie man in der Owncloud Gallery App die natürliche Sortierung von Dateien aktiviert

Owncloud hat mich wirklich von Anfang an begeistert – genauso gut wie Dropbox, aber auf dem eigenen Server. Die Clients auf den Rechnern arbeiten zuverlässig und tun genau, was sie sollen. Die mobilen Clients sind noch etwas verbesserungswürdig, die Owncloud Web Applikation hingegen ist schon ziemlich gut. Besonders gefallen hat mir die Gallery App, die Foto Ordner in schöne Galerien umwandelt. Sie hat aber ein gravierendes Problem: die Sortierung.

Meine Dateinamen in einem Ordner heißen beispielsweise:

Bild-1.jpg
Bild-2.jpg
Bild-3.jpg

Bild-10.jpg

Bild-20.jpg

Bild-100.jpg

In der Owncloud Datelliste werden sie auch so korrekt aufgelistet, in der Gallery App passiert jedoch folgendes:

Bild-1.jpg
Bild-10.jpg
Bild-100.jpg

Bild-2.jpg
Bild-20.jpg
Bild-3.jpg

Dieses Verhalten ist auf eine Javascript Funktion zurückzuführen und kann relativ einfach korrigiert werden. Editiert dazu einfach die Datei “apps/gallery/js/gallery.js”. Sucht nach der Zeile:

Gallery.fillAlbums = function () {
	var sortFunction = function (a, b) {
		return a.path.toLowerCase().localeCompare(b.path.toLowerCase());
	};

und ändert diese in

Gallery.fillAlbums = function () {
	var sortFunction = function (a, b) {
		return a.path.toLowerCase().localeCompare(b.path.toLowerCase(), 'de', {numeric: true});
	};

Und schon ist die Sortierung wieder so, wie erwartet.

[Quicktip] Wie man go get und Atlasssian Stash bzw. Gitlab zusammen bringt

In meiner Tätigkeit als Jenkins Admin hat mir das Thema „go get“ und Stash viel Kopfzerbrechen bereitet. Letztendlich ist „go get“ eine Art Alias für git pull, jedoch wird da einige Magic angewendet. Im Normalfall – also mit github und auch plain git Repos funktioniert das ganze wunderbar, sobald man aber Software wie Gitlab oder Stash verwendet, wird es etwas problematisch. Zum einen ist das „.git“, welches sowohl Gitlab als auch Stash an jedes Repo hängen ein Problem, zum anderen aber auch SSH über alternative Ports.

Zum Problem der „.git“ Endung: hängt einfach ein weiteres „.git“ an euren „go get“ Command. Also:

go get git.mydomain.com/foo/bar.git.git

Etwas kniffliger ist die Verwendung eines alternativen Ports und ssh. Stash macht nämlich genau das – der ssh Port ist 7999 in der Default Config. Aber auch hierfür gibt es eine Lösung. Dazu müsst ihr die Datei „.gitconfig“ in eurem Home Folder editieren bzw. anlegen. Die Datei muss dann so aussehen:

[url "ssh://git@stash.yourdomain.com:7999/"]
insteadOf = https://stash.yourdomain.de/

Sobald ihr nun einen Checkout auf

https://stash.yourdomain.de/foo/bar.git 

macht, biegt Git diesen Request auf

ssh://git@stash.yourdomain.com:7999/foo/bar.git

um. Somit könnt ihr auf andere Ports gehen, komplett auf andere Domains umschreiben usw.