Dart & Keycloak

I am so excited to read that the Keycloak JavaScript adapter (see http://www.keycloak.org/downloads.html) now comes bundled with TypeScript definitions. In combination with https://github.com/dart-lang/js_facade_gen it should be possible to generate Dart bindings for the Keycloak adapter. This means easy usage of OpenID Connect and OAuth2 authentication and authorization in Dart web projects using Keycloak for identity and access management.

I haven’t tested the generated bindings yet but I want to demonstrate how to generate the bindings.

  1. Install js_facade_gen
    npm install -g dart_js_facade_gen
  2. Download the Keycloak JavaScript Adapter
    wget https://downloads.jboss.org/keycloak/3.1.0.Final/adapters/keycloak-oidc/keycloak-js-adapter-dist-3.1.0.Final.zip
  3. Unzip the adapterunzip keycloak-js-adapter-dist-3.1.0.Final.zip
  4. Change into the folder
    cd keycloak-js-adapter-dist-3.1.0.Final
  5. Generate the bindings
    dart_js_facade_gen keycloak.d.ts > keycloak.dart

And here we see the generated bindings in all their glory. At the moment, the only caveat is that I don’t know if the generated bindings are functional.

@JS()
library keycloak;

import "package:js/js.dart";

/// Copyright 2017 Red Hat, Inc. and/or its affiliates
/// and other contributors as indicated by the @author tags.
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
/// http://www.apache.org/licenses/LICENSE-2.0
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.

// Module KeycloakModule
@anonymous
@JS()
abstract class Promise {
external Promise success(Function callback);
external Promise error(Function callback);
}

/*export type ResponseModes = "query" | "fragment";*/
/*export type Flows = "standard" | "implicit" | "hybrid";*/
@anonymous
@JS()
abstract class InitOptions {
external bool get checkLoginIframe;
external set checkLoginIframe(bool v);
external num get checkLoginIframeInterval;
external set checkLoginIframeInterval(num v);
external String get onLoad;
external set onLoad(String v);
external String get adapter;
external set adapter(String v);
external String /*'query'|'fragment'*/ get responseMode;
external set responseMode(String /*'query'|'fragment'*/ v);
external String /*'standard'|'implicit'|'hybrid'*/ get flow;
external set flow(String /*'standard'|'implicit'|'hybrid'*/ v);
external String get token;
external set token(String v);
external String get refreshToken;
external set refreshToken(String v);
external String get idToken;
external set idToken(String v);
external num get timeSkew;
external set timeSkew(num v);
external factory InitOptions(
{bool checkLoginIframe,
num checkLoginIframeInterval,
String onLoad,
String adapter,
String /*'query'|'fragment'*/ responseMode,
String /*'standard'|'implicit'|'hybrid'*/ flow,
String token,
String refreshToken,
String idToken,
num timeSkew});
}

@anonymous
@JS()
abstract class LoginOptions {
external String get redirectUri;
external set redirectUri(String v);
external String get prompt;
external set prompt(String v);
external num get maxAge;
external set maxAge(num v);
external String get loginHint;
external set loginHint(String v);
external String get action;
external set action(String v);
external String get locale;
external set locale(String v);
external factory LoginOptions(
{String redirectUri,
String prompt,
num maxAge,
String loginHint,
String action,
String locale});
}

@anonymous
@JS()
abstract class RedirectUriOptions {
external String get redirectUri;
external set redirectUri(String v);
external factory RedirectUriOptions({String redirectUri});
}

@anonymous
@JS()
abstract class KeycloakClient {
external Promise init([InitOptions options]);
external Promise login([LoginOptions options]);
external String createLoginUrl([LoginOptions options]);
external Promise logout([RedirectUriOptions options]);
external String createLogoutUrl([RedirectUriOptions options]);
external Promise register([LoginOptions options]);
external String createRegisterUrl([RedirectUriOptions options]);
external Promise accountManagement();
external String createAccountUrl([RedirectUriOptions options]);
external bool hasRealmRole(String role);
external bool hasResourceRole(String role, [String resource]);
external Promise loadUserProfile();
external bool isTokenExpired(num minValidity);
external Promise updateToken(num minValidity);
external dynamic clearToken();
external String get realm;
external set realm(String v);
external String get clientId;
external set clientId(String v);
external String get authServerUrl;
external set authServerUrl(String v);
external String get token;
external set token(String v);
external dynamic get tokenParsed;
external set tokenParsed(dynamic v);
external String get refreshToken;
external set refreshToken(String v);
external dynamic get refreshTokenParsed;
external set refreshTokenParsed(dynamic v);
external String get idToken;
external set idToken(String v);
external dynamic get idTokenParsed;
external set idTokenParsed(dynamic v);
external dynamic get realmAccess;
external set realmAccess(dynamic v);
external dynamic get resourceAccess;
external set resourceAccess(dynamic v);
external bool get authenticated;
external set authenticated(bool v);
external String get subject;
external set subject(String v);
external num get timeSkew;
external set timeSkew(num v);
external String /*'query'|'fragment'*/ get responseMode;
external set responseMode(String /*'query'|'fragment'*/ v);
external String /*'standard'|'implicit'|'hybrid'*/ get flow;
external set flow(String /*'standard'|'implicit'|'hybrid'*/ v);
external String get responseType;
external set responseType(String v);
external Function get onReady;
external set onReady(Function v);
external Function get onAuthSuccess;
external set onAuthSuccess(Function v);
external Function get onAuthError;
external set onAuthError(Function v);
external Function get onAuthRefreshSuccess;
external set onAuthRefreshSuccess(Function v);
external Function get onAuthRefreshError;
external set onAuthRefreshError(Function v);
external Function get onAuthLogout;
external set onAuthLogout(Function v);
external Function get onTokenExpired;
external set onTokenExpired(Function v);
}

// End module KeycloakModule
@JS()
class Keycloak {
// @Ignore
Keycloak.fakeConstructor$();
external factory Keycloak([dynamic config]);
}

Next step: Build a sample app and test the generated bindings.

When FortiClient messes up your DNS configuration

It happened to me that using FortClient destroyed my DNS configuration on my OSX (El Capitan).

I could reach webservers by IP but not by DNS. The reason for this is that FortClient registers a global DNS resolver and puts in the DNS servers configured for the VPN tunnel. If those DNS server don’t resolve „public“ domains you won’t be able to reach websites as you are used to.

To resolve this issue the scutil command comes to your help.

  1. Open the terminal application and issue the command
    sudo scutil
  2. Now you can see the scutil shell
  3. Check if there is a configuration for FortiClient by entering the following command:
    list State:/Network/Service/forticlientsslvpn/DNS

    The shell should print something like subKey [0] = State:/Network/Service/forticlientsslvpn/DNS. If FortClient hasn’t registered any DNS configuration you will see no keys.

  4. Assuming you didn’t get no keys you can have a peek on the current configuration by doing
    get State:/Network/Service/forticlientsslvpn/DNS
    d.show

    You will see something like

     {
      ConfirmedServiceID : forticlientsslvpn
      ServerAddresses :  {
        0 : xxx.xxx.xxx.xxx
        1 : yyy.yyy.yyy.yyy
      }
      SupplementalMatchDomains :  {
        0 :
      }
      SupplementalMatchOrders :  {
        0 : 100000
      }
    }
    

    Remember the IP addresses listet in the  section ServerAddresses.

  5. Now the reconfiguration takes place. Issue the following commands:
    d.init
    d.add ServerAddresses 8.8.8.8 8.8.4.4 <REMEMBERED IP ADDRESSES>
    set State:/Network/Service/forticlientsslvpn/DNS
    quit
    

    This will set the Google DNS servers as primary resolvers and the previously set DNS servers as secondary resolvers.

Tip: You can compare the configurations by executing

scutil --dns

before and after taking theses steps.

Unfortunately, these configuration doesn’t survive reboots and VPN re-connects. Therefore, I wrapped these configuration steps in a shell script to automate these tasks.

#!/bin/bash
scutil <<EOF
d.init
d.add ServerAddresses 8.8.8.8 8.8.4.4 <REMEMBERED IP ADDRESSES>
set State:/Network/Service/forticlientsslvpn/DNS
quit
EOF

This script has to be executed with sudo.

Getting ffmpeg to work with libfaac on Fedora 21 to be able to stream a video to a Chromecast device

I wanted to use the script https://gist.github.com/steventrux/10815095
to convert some movie files to be compatible with Chromecast. Unfortunately, ffmpeg does not support encoding audio to AAC in Fedora by default.

All I had to do was to recompile ffmpeg with libfaac-support and adjust the conversion script a little bit. The process of rebuilding is based on the post http://tinkerance.blogspot.co.at/2012/11/getting-ffmpeg-to-work-with-libfaac-on.html

# change to the home directory
$ cd

# download the source rpm for ffmpeg
$ yumdownloader --source ffmpeg

# unpack the source rpm
#
# replace the correct file name of ffmpeg-*.src.rpm
# by the name displayed at the end of the preceeding command
# rpm -ivh ffmpeg-2.4.8-3.fc21.src.rpm

# download build dependencies
$ sudo yum-builddep ~/rpmbuild/SPECS/ffmpeg.spec

# download build dependency for AAC encoding
$ sudo yum install faac-devel

# compile and build the new ffmpeg rpm
$ rpmbuild -ba ~/rpmbuild/SPECS/ffmpeg.spec --with faac

# uninstall the old version of ffmpeg
$ sudo yum remove ffmpeg.x86_64

# install the new rpm package
$ sudo yum install ~/rpmbuild/RPMS/x86_64/ffmpeg-*

This is the slightly modified conversion script

#! /bin/bash

# Batch Convert Script by StevenTrux
# The Purpose of this Script is to batch convert any video file to mp4 or mkv format for chromecast compatibility
# this script only convert necessary tracks if the video is already
# in H.264 format it won't convert it saving your time!

# Put all video files need to be converted in a folder!
# the name of files must not have &quot; &quot; Space!
# Rename the File if contain space 

# Variable used:
# outmode should be mp4 or mkv
# sourcedir is the directory where to be converted videos are
# indir is the directory where converted video will be created

# usage:
#########################
# cast.sh mp4 /home/user/divx /home/user/chromecastvideos
# or
# cast.sh mkv /home/user/divx /home/user/chromecastvideos
#########################

# working mode
outmode=$1
# check output mode
if [[ $outmode ]]; then
if [ $outmode = &quot;mp4&quot; ] || [ $outmode = &quot;mkv&quot; ]
	then
	echo &quot;WORKING MODE $outmode&quot;
	else
	echo &quot;$outmode is NOT a Correct target format. You need to set an output format! like cast.sh mp4 xxxx or cast.sh mkv xxxx&quot;
	exit
fi
else
echo &quot;Working mode is missing. You should set a correct target format like mp4 or mkv&quot;
exit
fi

# Source dir
sourcedir=$2
if [[ $sourcedir ]]; then
     echo &quot;Using $sourcedir as Input Folder&quot;
	else
	 echo &quot;Error: Check if you have set an input folder&quot;
	 exit
fi

# Target dir
indir=$3
if [[ $indir ]]; then
if mkdir -p $indir/castable
	then
	 echo &quot;Using $indir/castable as Output Folder&quot;
	else
	 echo &quot;Error: Check if you have the rights to write in $indir&quot;
	 exit
fi
	else
	 echo &quot;Error: Check if you have set an output folder&quot;
	 exit
fi

# set format
if [ $outmode=mp4 ]
	then
	 outformat=mp4
	else
	 outformat=matroska
fi

# Check FFMPEG Installation
if ffmpeg -formats &gt; /dev/null 2&gt;&amp;1
	then
	 ffversion=`ffmpeg -version 2&gt; /dev/null | grep ffmpeg | sed -n 's/ffmpeg\s//p'`
	 echo &quot;Your ffmpeg verson is $ffversion&quot;
	else
	 echo &quot;ERROR: You need ffmpeg installed with x264 and aac encoder&quot;
	 exit
fi

if ffmpeg -formats 2&gt; /dev/null | grep &quot;E mp4&quot; &gt; /dev/null
	then
	 echo &quot;Check mp4 container format ... OK&quot;
	else
	 echo &quot;Check mp4 container format ... NOK&quot;
	 exit
fi

if ffmpeg -formats 2&gt; /dev/null | grep &quot;E matroska&quot; &gt; /dev/null
        then
         echo &quot;Check mkv container format ... OK&quot;
        else
         echo &quot;Check mkv container format ... NOK&quot;
         exit
fi

#if ffmpeg -codecs 2&gt; /dev/null | grep &quot;libfdk_aac&quot; &gt; /dev/null
if ffmpeg -codecs 2&gt; /dev/null | grep &quot;aac&quot; &gt; /dev/null
        then
         echo &quot;Check AAC Audio Encoder ... OK&quot;
        else
         echo &quot;Check AAC Audio Encoder ... NOK&quot;
         exit
fi

#if ffmpeg -codecs 2&gt; /dev/null | grep &quot;libx264&quot; &gt; /dev/null
if ffmpeg -codecs 2&gt; /dev/null | grep &quot;h264&quot; &gt; /dev/null
        then
         echo &quot;Check x264 the free H.264 Video Encoder ... OK&quot;
        else
         echo &quot;Check x264 the free H.264 Video Encoder ... NOK&quot;
         exit
fi

echo &quot;Your FFMpeg is OK Entering File Processing&quot;

################################################################
cd &quot;$sourcedir&quot;
for filelist in `ls`
do
	if ffmpeg -i $filelist 2&gt;&amp;1 | grep 'Invalid data found'		#check if it's video file
	   then
	   echo &quot;ERROR File $filelist is NOT A VIDEO FILE can be converted!&quot;
	   continue	   

	fi

	if ffmpeg -i $filelist 2&gt;&amp;1 | grep Video: | grep h264		#check video codec
	   then
	    vcodec=copy
	   else
	    vcodec=libx264
	fi

	if ffmpeg -i $filelist 2&gt;&amp;1 | grep Video: | grep &quot;High 10&quot;	#10 bit H.264 can't be played by Hardware.
	   then
	    vcodec=libx264
	fi

	if [ ffmpeg -i $filelist 2&gt;&amp;1 | grep Audio: | grep aac ] || [ 	ffmpeg -i $filelist 2&gt;&amp;1 | grep Audio: | grep mp3 ]	#check audio codec
	   then
	    acodec=copy
	   else
	    acodec=aac
	fi

        echo &quot;Converting $filelist&quot;
	echo &quot;Video codec: $vcodec Audio codec: $acodec Container: $outformat&quot; 

# using ffmpeg for real converting
	echo &quot;ffmpeg -i $filelist -y -f $outformat -acodec $acodec -ab 192k -ac 2 -absf aac_adtstoasc -async 1 -vcodec $vcodec -vsync 0 -profile:v main -level 3.1 -qmax 22 -qmin 20 -x264opts no-cabac:ref=2 -threads 0 $indir/castable/$filelist.$outmode&quot;
	ffmpeg -i $filelist -y -f $outformat -acodec $acodec -ab 192k -ac 2 -absf aac_adtstoasc -async 1 -vcodec $vcodec -vsync 0 -profile:v main -level 3.1 -qmax 22 -qmin 20 -x264opts no-cabac:ref=2 -threads 0 -strict -2 $indir/castable/$filelist.$outmode

done
	echo ALL Processed!

###################
echo &quot;DONE, your video files are chromecast ready&quot;
exit

For easy streaming I recommend to convert the videos to mp4, open them in the Google Chrome browser and stream the tab to the Chromecast.

Fedora does not boot with the latest installed kernel

I had the problem that updating Fedora installed a new kernel but the system never booted the new kernel. The reason is that grub2 does not reflect the changes.

To fix the problem, you simply have to execute the following command as root to obtain the grub2 configuration utilities and update the grub configuation:

dnf install grub2-tools
grub2-mkconfig -o /boot/grub2/grub.cfg

Fedora should now boot again with that latest installed kernel.

Using magit-status from the command line

Often, I want to use magit as a convenience front-end to git directly from the command line (bash) without calling M-x magit-status after Emacs start up.

The following code snippet opens Emacs without X support, starts Magit for the current directory and makes Magit the only window in Emacs:

emacs -nw --eval "(magit-status nil 'switch-to-buffer)(delete-other-windows)"

Since executing such a long command line is really cumbersome I created a bash alias as

alias magit-status='emacs -nw --eval \"(magit-status nil \'switch-to-buffer\)\(delete-other-windows\)\"'

From now on, I can use Magit by issuing magit-status directly on the command line. If you want to have this command permanently available just add the alias definition to your .bashrc file.

VirtualBox causes „kernel panic – not syncing: fatal exception in interrupt“ on the host

This error happens to me when the virtual machine is located on a fuse mounted file system like my external USB harddisk with NTFS. My investigations revealed that this must have something to do with https://bbs.archlinux.org/viewtopic.php?id=185841 and https://bugzilla.kernel.org/show_bug.cgi?id=82951.

Indeed, replacing the NTFS file system on my external harddisk with EXT4 solved the problem.

Skype on Chrome OS / Chromebook / Chrome

The official documentation states that Skype is not supported on Chromebooks. This changed since the availability of ARChon Custom Runtime. All you have to do for a functional Skype installation on Chromebooks and in fact every device running the Google Chrome Browser is

  1. Install Skype on an Android device from the Google Playstore (https://play.google.com/store/apps/details?id=com.skype.raider)
  2. Install ARChon Packager on the same Android device (https://play.google.com/store/apps/details?id=me.bpear.archonpackager)
  3. Start ARChon Packager and select the installed Skype app for conversion to the ARChon runtime
  4. Copy the created zip file to your Chromebook (I used Google Drive and the share button in the ARChon Packager)
  5. Download the ARChon runtime on your Chromebook from https://github.com/vladikoff/chromeos-apk/blob/master/archon.md
  6. Unzip it
  7. Open the Chrome browser and visit chrome://extensions
  8. Click on „Load unpacked extension…“ and select the directory containing the runtime you previously unzipped
  9. Download the Skype zip file
  10. Unzip it
  11. Open the Chrome browser and visit chrome://extensions
  12. Click on „Load unpacked extension…“ and select the directory containing Skype you previously unzipped
  13. Congratulations, you can now launch Skype from the apps launcher

Chrome with the ARChon and Skype extension Test call on Skype

Aktiv Entscheidungen treffen