diff --git a/.gitignore b/.gitignore
index ca089f967a73f425cf203e4d8d4ea5339ebf6e4b..2b083ce9aec60dee499bc227be0e5fa04f61d3c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,4 +16,9 @@ nbproject
 /database/logs.db
 /public_html/deployment/dummy.db
 /public_html/deployment/classes/ramdb.class.php
-/public_html/~cache/
\ No newline at end of file
+/public_html/~cache/
+/public_html/deployment/classes/spooler-handler.class.php
+/public_html/deployment/pages/act_jstest.php
+/public_html/deployment/classes/html-adminltetest.tpl.php
+/public_html/deployment/adminlte/
+/config/inc_user2projects.php
\ No newline at end of file
diff --git a/config/inc_roles.php b/config/inc_roles.php
new file mode 100644
index 0000000000000000000000000000000000000000..3c1b62437c6917085e0dc6e3d178926630f68b36
--- /dev/null
+++ b/config/inc_roles.php
@@ -0,0 +1,84 @@
+<?php
+/*
+ * IML DEPLOYMENT GUI
+ * ROLES and its permissions
+ * 
+ * - page_login permission for all to show login form
+ *
+ */
+
+return array(
+    "all" => array(
+        "page_login"
+    ),
+    "authenticated" => array(
+        "page_overview",
+        
+        "page_build",
+        "page_cleanup",
+        "page_setup",
+        
+        // see $oProject->renderLink() and $oProject->[äction]
+        "project-action-default",
+        "project-action-accept-preview",
+        "project-action-build",
+        "project-action-cleanup",
+        "project-action-deploy-preview",
+        "project-action-overview",
+        "project-action-phase",
+        "project-action-setup",
+    ),
+    
+    
+    "admin" => array(
+        
+        "page_build",
+        "page_cleanup",
+        "page_delete",
+        "page_setup",
+        
+        // see $oProject->renderLink() and $oProject->[äction]
+        "project-action-default",
+        "project-action-accept",
+        "project-action-build",
+        "project-action-cleanup",
+        "project-action-create",
+        "project-action-deploy",
+        "project-action-new",
+        "project-action-overview",
+        "project-action-phase",
+        "project-action-setup",
+    ),
+    
+    // ----- wenn es mal eine feinere Granulierung braucht, muss man eine
+    //       User-Admin programmieren
+    
+    /*
+    "authenticated_" => array(
+        "page_overview",
+    ),
+    "developer_" => array(
+        "page_build",
+        "page_cleanup",
+        "page_setup",
+        // see $oProject->renderLink() and $oProject->[äction]
+        "project-action-default",
+        "project-action-accept-preview",
+        "project-action-build",
+        "project-action-cleanup",
+        "project-action-deploy-preview",
+        "project-action-overview",
+        "project-action-phase",
+        "project-action-setup",
+    ),
+    "projectmanager_" => array(
+        "project-action-default",
+        "project-action-accept-preview",
+        "project-action-accept-stage",
+        // "project-action-deploy",
+        "project-action-overview",
+        "project-action-phase",
+    ),
+    */
+    
+);
diff --git a/config/inc_user2roles.php b/config/inc_user2roles.php
new file mode 100644
index 0000000000000000000000000000000000000000..19e2b5621bbcc7d0234e861b9c581c494f1b185f
--- /dev/null
+++ b/config/inc_user2roles.php
@@ -0,0 +1,18 @@
+<?php
+/*
+ * IML DEPLOYMENT GUI
+ * USERS and assigned roles
+ * 
+ * remark: Every user logged in is member of "authenticated"
+ */
+
+return array(
+    // "authenticated" => array(),
+    // "developer" => array(),
+    // "projectmanager" => array(),
+    "admin" => array(
+        "hahn",
+        "dschueler",
+    ),
+    
+);
diff --git a/config/lang/de.json b/config/lang/de.json
index b6379dace8cb85a2517861a68ab8abcc8641fca0..d24fa994bea92439c53095d54f96369433ff6c19 100644
--- a/config/lang/de.json
+++ b/config/lang/de.json
@@ -13,6 +13,7 @@
     "menu-project-settings": "Projekteinstellungen",
     "menu-project-delete": "Projekt l&ouml;schen",
     "menu-project-phases": "Phasen",
+    "menu-login": "Anmelden/ Abmelden",
     "menu-help": "Hilfe",
     "menu-help-classes": "verwendete Klassen",
     
@@ -181,6 +182,15 @@
     "page-deploy-info-ignore-deploytime": "Deploy-Zeitfenster ignorieren.<br>Nicht jedes Update muss sauber durchlaufen. Nur aktivieren, wenn einer der Entwickler und/ oder ein Sysadmin zur Hand ist.",
     "page-deploy-buttonlabel": "Auf [%s] ausrollen",
     
+    "page-login-info": "Anmeldung",
+    "page-login-info-introtext": "Sie sind nicht angemeldet. Geben Sie Benutzernamen und Passwort ein, um sich anzumelden.",
+    "page-login-auth-failed": "Tut mir leid, die Anmeldung ist fehlgeschlagen.",
+    "page-login-userloggedin": "Sie sind mit den folgenden Benutzernamen angemeldet:",
+    "page-login-usergroups": "Ihnen zugeordnete Gruppen:",
+    "page-login-userprojects": "... und Projekte:",    
+    "page-login-username": "Benutzername",
+    "page-login-password": "Passwort",
+    
     "page-overview-no-phase": "Es wurde noch keine URL in keiner der Phasen definiert",
     "page-overview-phase-infos": "F&uuml;r dieses Projekt sind folgende Phasen konfiguriert:",
     
@@ -232,6 +242,8 @@
     "inactive": "inaktiv",
     "info": "Info",
     "infos": "Infos",
+    "login": "Anmelden",
+    "logoff": "Abmelden",
     "ok": "OK",
     "new-project": "neues Projekt",
     "new-project-hint": "ein neues Projekt anlegen",
diff --git a/config/lang/en.json b/config/lang/en.json
index c5d69d34108ed90a94649a231cd039262ff27bf0..5ea90cf4fe0be92e3c4ccb76c51cf6564a1d9351 100644
--- a/config/lang/en.json
+++ b/config/lang/en.json
@@ -12,6 +12,7 @@
     "menu-project-settings": "Project settings",
     "menu-project-delete": "Delete project",
     "menu-project-phases": "Phases",
+    "menu-login": "Login/ Logoff",
     "menu-help": "Help",
     "menu-help-classes": "Used classes",
     
@@ -182,6 +183,15 @@
     "page-deploy-info-ignore-deploytime": "Ignore deploy time window.<br>Not each update mus run successfully. Do not activate this option if no developer and/ or a sysadmin is available.",
     "page-deploy-buttonlabel": "install to [%s]",
 
+    "page-login-info": "Login",
+    "page-login-info-introtext": "You are not logged in. Enter your username and password.",
+    "page-login-auth-failed": "Sorry the authentication failed.",
+    "page-login-userloggedin": "You are logged in as the following user:",
+    "page-login-usergroups": "You are in the following groups:",
+    "page-login-userprojects": "... and these projects:",    
+    "page-login-username": "Username",
+    "page-login-password": "Password",
+    
     "page-overview-no-phase": "This project has no active phase. No url was entered to any phase.",
     "page-overview-phase-infos": "This project has these phases:",
     
@@ -234,6 +244,8 @@
     "inactive": "inactive",
     "info": "Info",
     "infos": "Infos",
+    "login": "Login",
+    "logoff": "Logoff",
     "ok": "OK",
     "new-project": "new project",
     "new-project-hint": "create a new project",
diff --git a/public_html/deployment/classes/actionlog.class.php b/public_html/deployment/classes/actionlog.class.php
index 4afecb48d17207514a765269e47885a75711e801..0a8991616d26ef670de108649e8d8e07f38e0602 100644
--- a/public_html/deployment/classes/actionlog.class.php
+++ b/public_html/deployment/classes/actionlog.class.php
@@ -58,6 +58,7 @@ class Actionlog {
      * create sqlite database - called in constructor if the file does not exist
      */
     private function _createDb() {
+        echo "try to create file $this->_dbfile ...<br>\n";
         return $this->_makeQuery($this->_sCreate);
     }
 
diff --git a/public_html/deployment/classes/base.class.php b/public_html/deployment/classes/base.class.php
index 8ef4c3559a3e833105a9312dbe5aecf0c54ded5a..0a647f270142ef7d438d24d78bac841bff94d873 100644
--- a/public_html/deployment/classes/base.class.php
+++ b/public_html/deployment/classes/base.class.php
@@ -14,13 +14,9 @@ class base {
     /**
      * init user with optional given user
      * @param type $sUser
-     */
-    public function __construct(){
+    public function __construct($a=false){
         $this->oUser=new user();
-        
-        if (method_exists($this, "_construct2")){
-            $this->_construct2();
-        }
     }
+     */
     
 }
diff --git a/public_html/deployment/classes/formgen.class.php b/public_html/deployment/classes/formgen.class.php
index 24cc4ebde35452b56bb6aa83e6e3f708dfcf0aff..bff052c643fdac5bbfd2c466bdc96426d015ebd4 100644
--- a/public_html/deployment/classes/formgen.class.php
+++ b/public_html/deployment/classes/formgen.class.php
@@ -249,8 +249,9 @@ class formgen {
                 break;
 
             case "text":
+            case "password":
                 $this->_checkReqiredKeys($elementData, array("name"));
-                $sFormElement.='    <input type="text" id="' . $sId . '" class="form-control col-sm-10" ';
+                $sFormElement.='    <input type="'.$elementData["type"].'" id="' . $sId . '" class="form-control col-sm-10" ';
                 $aAllowedHtmlAttributes["text"] = explode(",", "");
                 $sFormElement.=$this->_addHtmlAtrributes(explode(",", "$sDefaultAttributes,name,list,disabled,onkeyup,onkeydown,onchange,pattern,placeholder,required,size,value"), $elementData);
                 // $sFormElement.=$this->_addHtmlAtrributes(array("name", "value", "size", "placeholder", "required"), $elementData);
diff --git a/public_html/deployment/classes/ldap.class.php b/public_html/deployment/classes/ldap.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..4d9b1f3ed428aebdc170735fb4d74f8160e69ab9
--- /dev/null
+++ b/public_html/deployment/classes/ldap.class.php
@@ -0,0 +1,474 @@
+<?php
+
+/**
+ * IML LDAP CONNECTOR FOR USER AUTHENTICATION
+ *
+ * @author axel.hahn@iml.unibe.ch
+ */
+class imlldap {
+
+    private $_aLdap = array(
+        'server'       => false,
+        'port'         => false,
+        'DnLookupUser' => false, // ldap rdn oder dn
+        'PwLookupUser' => false,
+        'DnUserNode'   => false,   // ou=People...
+        'DnAppNode'    => false,   // cn=AppGroup...
+        'protoVersion' => 3,
+        'debugLevel'   => 0,
+    );
+    private $_ldapConn = false;
+    private $_ldapBind = false;
+    var $bDebug = false;
+
+    /**
+     * constructor
+     * @param array  $aConfig  optional set ldap connection
+     */
+    public function __construct($aConfig = array()) {
+        if (!function_exists("ldap_connect")){
+            die(__CLASS__ . " ERROR: php-ldap module is not installed on this server.");
+        }
+        if (count($aConfig)) {
+            $this->setConfig($aConfig);
+        }
+    }
+
+    public function __destruct() {
+        $this->close();
+    }
+
+
+    // ----------------------------------------------------------------------
+    // write debug text
+    // ----------------------------------------------------------------------
+    
+    /**
+     * turn debug messages on;
+     * if this detail level is not enough, set a value with key debugLevel in 
+     * ldap config array
+     * @see setConfig()
+     */
+    public function debugOn(){
+        $this->bDebug=true;
+        if($this->_aLdap['debugLevel']){
+            $this->_w(__FUNCTION__ . ' setting debug level ' . $this->_aLdap['debugLevel']);
+            ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, $this->_aLdap['debugLevel']);
+        }
+    }
+    
+    /**
+     * turn debug messages off
+     */
+    public function debugOff(){
+        $this->bDebug=false;
+        ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 0);
+    }
+    
+
+    private function _w($sText) {
+        if (!$this->bDebug) {
+            return false;
+        }
+        echo __CLASS__ . ' DEBUG: ' . $sText . "<br>\n";
+        return true;
+    }
+
+    // ----------------------------------------------------------------------
+    // setup
+    // ----------------------------------------------------------------------
+
+    /**
+     * set a ldap config 
+     * 
+     * @param array  $aConfig   new config items
+     *             'server'       => 'ldaps://ldap.example.com',
+     *             'port'         => 636,
+     *             'DnLookupUser' => 'cn=Lookup,ou=ServiceAccounts,dc=org,dc=example.com',     // ldap rdn oder dn
+     *             'PwLookupUser' => 'IkHEFFzlZ...99j0h8WdI0LrLhxU',  // password
+     *             'DnUserNode'   => 'ou=People,ou=ORG,dc=org,dc=example.com',
+     *             'DnAppNode'    => '' optional dn ... if a user must be member of a given group
+     *             'protoVersion' => 3
+     *             'debugLevel'   => 0 // for debugging set higher 0 AND call debugOn()
+     */
+    public function setConfig($aConfig = array()) {
+        if (is_array($aConfig)) {
+            foreach (array_keys($this->_aLdap) as $sKey) {
+                if (array_key_exists($sKey, $aConfig)) {
+                    $this->_w(__FUNCTION__ . ' setting ldap '.$sKey.' = '. $aConfig[$sKey]);
+                    $this->_aLdap[$sKey] = $aConfig[$sKey];
+                }
+            }
+        }
+        
+    }
+
+    // ----------------------------------------------------------------------
+    // ldap lowlevel functions
+    // ----------------------------------------------------------------------
+
+    /**
+     * close an existing ldap connection
+     */
+    public function close() {
+        if ($this->_ldapConn) {
+            $this->_w(__FUNCTION__ . ' closing connection.');
+            ldap_close($this->_ldapConn);
+        } else {
+            $this->_w(__FUNCTION__ . ' SKIP close.');
+        }
+
+        $this->_ldapConn = false;
+    }
+
+    /**
+     * connect to ldap
+     */
+    public function connect() {
+
+        if (!array_key_exists('server', $this->_aLdap) || !$this->_aLdap['server']) {
+            die(__CLASS__ . " ERROR: no ldap server was setup set. Use setConfig() first.");
+        }
+
+        if ($this->_ldapConn) {
+            $this->close();
+        }
+
+        $this->_w(__FUNCTION__ . ' connect to ' . $this->_aLdap['server'] . ':' . $this->_aLdap['port']);
+        $this->_ldapConn = ldap_connect($this->_aLdap['server'], $this->_aLdap['port']);
+        if (!$this->_ldapConn) {
+            die(__CLASS__ . " ERROR: ldap connect failed.");
+        }
+        $this->_w(__FUNCTION__ . ' OK, connected.');
+        
+        if($this->_aLdap['protoVersion']){
+            $this->_w(__FUNCTION__ . ' setting protocol version .' . $this->_aLdap['protoVersion']);
+            ldap_set_option($this->_ldapConn, LDAP_OPT_PROTOCOL_VERSION, $this->_aLdap['protoVersion']);
+        }
+        
+        ldap_set_option($this->_ldapConn, LDAP_OPT_REFERRALS, 0); // for AD MS Windows 
+    }
+
+    /**
+     * ldap bind connects with a ldap user. 
+     * If the ldap connection was not opened yet the connection will be established.
+     * If a binding exists it will be unbind
+     * 
+     * @see connect()
+     * @see unbind()
+     * 
+     * @param string  $sUser   username
+     * @param string  $sPw     password
+     */
+    public function bind($sUser, $sPw='') {
+        
+        if(!$this->_ldapConn){
+            $this->connect();
+        }
+        if($this->_ldapBind){
+            $this->unbind();
+        }
+
+        if(!$sUser){
+            $this->_w(__FUNCTION__ . ' ERROR: no user was set as first param.');
+            die("ERROR: no user was given to connect to ldap.");
+        }
+        $this->_w(__FUNCTION__ . ' with user ' . $sUser. ' PW '.$sPw);
+        
+        $this->_ldapBind = @ldap_bind($this->_ldapConn, $sUser, $sPw);
+        if (!$this->_ldapBind) {
+            $this->_w(__FUNCTION__ . ' failed with er error ' . ldap_error($this->_ldapConn));
+            return false;
+        }
+        $this->_w(__FUNCTION__ . ' OK, successful.');
+        return true;
+    }
+
+    /**
+     * ldap unbind ... if a bind exists
+     */
+    public function unbind() {
+        if ($this->_ldapBind && !is_bool($this->_ldapBind)) {
+            $this->_w(__FUNCTION__ . ' ...');
+            ldap_unbind($this->_ldapBind);
+        } else {
+            $this->_w(__FUNCTION__ . ' SKIP.');
+        }
+        $this->_ldapBind = false;
+    }
+
+    // ----------------------------------------------------------------------
+    // ldap highlevel functions
+    // ----------------------------------------------------------------------
+
+    /**
+     * search in ldap directory and get result as array
+     * 
+     * @param string  $sSearchFilter     filter in ldap filter syntax
+     * @param array   $aAttributesToGet  flat array of attributes to fetch
+     * @return array
+     */
+    public function search($sSearchFilter, $aAttributesToGet=array("*")) {
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+    
+        $this->_w(__FUNCTION__ . ' DN = '.$this->_aLdap['DnUserNode'] . ' filter = '.$sSearchFilter);
+        
+        $oLdapSearch = ldap_search(
+                $this->_ldapConn, 
+                $this->_aLdap['DnUserNode'], 
+                $sSearchFilter, 
+                $aAttributesToGet
+        );
+
+        $aItems = $oLdapSearch?ldap_get_entries($this->_ldapConn, $oLdapSearch):false;
+        return $aItems;
+    }
+
+    /**
+     * search user by a given username or email address. 
+     * It returns false if the user does not exist or is
+     * not member of the group 'DnAppNode' (if it was set).
+     * 
+     * @param type $sUser             user id or email to search
+     * @param type $aAttributesToGet  i.e. array("ou", "sn", "vorname", "mail", "uid", "memberOf")
+     * @return boolean
+     */
+    public function getUserInfo($sUser, $aAttributesToGet=array("*")) {
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+
+        // generate search filter
+        $sSearchFilter = (strpos($sUser, '@'))?"(mail=$sUser)" : "(uid=$sUser)";
+        if($this->_aLdap['DnAppNode']){
+            $sSearchFilter.='(memberof='.$this->_aLdap['DnAppNode'] .')';
+        }
+        $sSearchFilter='(&'.$sSearchFilter.')';
+        
+        $aItems = $this->search($sSearchFilter, $aAttributesToGet);
+
+        if(count($aItems)==2){
+            $this->_w(__FUNCTION__ . ' OK: I got a single result: ' . print_r($aItems[0],1) );
+            return $aItems[0];
+        }
+        return false;        
+    }
+    
+    /**
+     * search for a DN entry with the lookup user by a given username or
+     * email address. It returns false if the user does not exist or is
+     * not member of the group 'DnAppNode' (if it was set).
+     * 
+     * @param type $sUser
+     * @return string
+     */
+    public function getUserDn($sUser) {
+        $this->_w(__FUNCTION__ . '('.$sUser.')');
+        $aItem=$this->getUserInfo($sUser, array("dn"));
+        if(is_array($aItem) && array_key_exists('dn', $aItem)){
+            $this->_w(__FUNCTION__ . ' OK: dn was found ' . $aItem['dn']);
+            return $aItem['dn'];
+        }
+        return false;        
+    }
+
+    /**
+     * set a password for a given user; 
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sUser  username or email
+     * @param string  $sPW    password
+     * @return boolean
+     */
+    public function setPassword($sUser, $sPW){
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        $sDn=$this->getUserDn($sUser);
+        if ($sDn){
+            return ldap_mod_replace ($this->_ldapConn, $sDn, array('userpassword' => "{MD5}".base64_encode(pack("H*",md5($sPW)))));
+        }
+        $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser);
+        return false;
+    }
+    
+    /**
+     * update an ldap object
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sDn     dn to update
+     * @param string  $aItem   array of new ldap properties
+     * @return boolean
+     */
+    public function objAdd($sDn, $aItem){
+        $this->_w(__FUNCTION__ . '("'.$sDn.'", [array])');
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        return ldap_add($this->_ldapConn, $sDn, $aItem);
+    }
+    
+    /**
+     * update an ldap attribute
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sDn     dn to update
+     * @param string  $aItem   array of new ldap properties
+     * @return boolean
+     */
+    public function objAddAttr($sDn, $aItem){
+        $this->_w(__FUNCTION__ . '("'.$sDn.'", [array])');
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        if ($sDn && is_array($aItem)){
+            $this->_w(__FUNCTION__ . ' ' . $this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect');
+            $this->_w(__FUNCTION__ . ' ldap_mod_add($this->_ldapConn, "'.$sDn.'", ' . print_r($aItem,1) . ')');
+            if (!ldap_mod_add($this->_ldapConn, $sDn, $aItem)){
+                $this->_w(__FUNCTION__ . ' ERROR: ' . ldap_error($this->_ldapConn));
+                return false;
+            } return true;
+        } 
+        $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not ann array ' . print_r($aItem,1));
+        return false;
+    }
+    
+    /**
+     * update an ldap object
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sDn    full DN where to update the item
+     * @param array   $aItem  updated entry
+     * @return boolean
+     */
+    public function objUpdate($sDn, $aItem){
+        $this->_w(__FUNCTION__ . '("'.$sDn.'", [array])');
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        if ($sDn && is_array($aItem)){
+            return ldap_mod_replace($this->_ldapConn, $sDn, $aItem);
+        } 
+        $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) ' . print_r($aItem,1));
+        return false;
+    }
+
+    
+    /**
+     * delete an ldap object
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sDn    full DN to remove 
+     * @return boolean
+     */
+    public function objDelete($sDn){
+        $this->_w(__FUNCTION__ . '("'.$sDn.'")');
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        
+        if ($sDn){
+            if (!ldap_delete($this->_ldapConn, $sDn)){
+                $this->_w(__FUNCTION__ . ' ERROR: ' . ldap_error($this->_ldapConn));
+                return false;
+            } return true;
+        } 
+        $this->_w(__FUNCTION__ . ' missing parameter for DN');
+        return false;
+    }
+    
+    /**
+     * delete attributes of an ldap object
+     * this requires a ldap bind with master/ admin account
+     * 
+     * TODO: Test me
+     * 
+     * @param string  $sDn    DN
+     * @param string  $aItem  item to remove
+     * @return boolean
+     */
+    public function objDeleteAttr($sDn, $aItem){
+        $this->_w(__FUNCTION__ . '("'.$sDn.'", [array])');
+        if (!$this->_ldapBind) {
+            $this->bind($this->_aLdap['DnLookupUser'], $this->_aLdap['PwLookupUser']);
+        }
+        if ($sDn && is_array($aItem)){
+            $this->_w(__FUNCTION__ . ' ' . $this->_ldapConn ? 'Verbindung da' : 'kein LDAP Connect');
+            $this->_w(__FUNCTION__ . ' ldap_mod_del($this->_ldapConn, "'.$sDn.'", ' . print_r($aItem,1) . ')');
+            if (!ldap_mod_del($this->_ldapConn, $sDn, $aItem)){
+                $this->_w(__FUNCTION__ . ' ERROR: ' . ldap_error($this->_ldapConn));
+                return false;
+            } return true;
+        } 
+        $this->_w(__FUNCTION__ . ' dn not found (item does not exist in ldap) or item was not ann array ' . print_r($aItem,1));
+        return false;
+    }
+    
+    /**
+     * create a new user item
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param array   $aItem  ldap properties
+     * @param string  $sDn    optional DN where to create the user
+     * @return boolean
+     */
+    public function userAdd($aItem, $sDn=false){
+        if (!$sDn){
+            $sDn='cn='.$aItem['cn'].','.$this->_aLdap['DnUserNode'];
+        }
+        $this->_w(__FUNCTION__ . '([array], "'.$sDn.'")');
+        if ($sDn){
+            return $this->objAdd($sDn, $aItem);
+        } 
+        $this->_w(__FUNCTION__ . ' node dn where to put the user was not found; set a value DnUserNode in ldap config or set it as 2nd parameter ' . print_r($aItem,1));
+        return false;
+    }
+    
+    /**
+     * delete a user
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sUser  user to update
+     * @param string  $sPW    new password to set
+     * @return boolean
+     */
+    public function userDelete($sUserDn){
+        $this->_w(__FUNCTION__ . '('.$sUserDn.')');
+        return $this->objDelete($sUserDn);
+    }
+    
+    /**
+     * update an ldap object
+     * this requires a ldap bind with master/ admin account
+     * 
+     * @param string  $sUser  user to update
+     * @param string  $sPW    new password to set
+     * @return boolean
+     */
+    public function userUpdate($aItem){
+        $this->_w(__FUNCTION__ . '([array])');
+        $sDn=$this->getUserDn($aItem['uid']);
+        if ($sDn){
+            return $this->objUpdate($sDn, $aItem);
+        } 
+        $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sDn);
+        return false;
+    }
+    /**
+     * verify user and password
+     * @param string  $sUser  username or email
+     * @param string  $sPW    password
+     * @return boolean
+     */
+    public function verifyPassword($sUser, $sPW){
+        $sDn=$this->getUserDn($sUser);
+        if ($sDn){
+            return $this->bind($sDn, $sPW);
+        }
+        $this->_w(__FUNCTION__ . ' dn not found (user does not exist in ldap) ' . $sUser);
+        return false;
+    }
+    
+}
diff --git a/public_html/deployment/classes/project.class.php b/public_html/deployment/classes/project.class.php
index bc3bd40658d35dbe9765a4e5b11e391b252c7b10..282bcfb66c6f40bdca5fffa068a98095e8280435 100644
--- a/public_html/deployment/classes/project.class.php
+++ b/public_html/deployment/classes/project.class.php
@@ -91,6 +91,7 @@ class project extends base {
      * @param string $sId  id of the project
      */
     public function __construct($sId = false) {
+        $this->oUser=new user();
         $this->_readConfig();
         if ($sId) {
             $this->setProjectById($sId);
@@ -545,6 +546,9 @@ class project extends base {
      */
     public function cleanupArchive($bDeleteAll = false) {
         $aUnused = array();
+        if (!$this->oUser->hasPermission("project-action-cleanup")){
+            return $this->oUser->showDenied();
+        }
         $sDir = $this->_getProjectArchiveDir();
         $this->getVersions();
 
@@ -583,6 +587,9 @@ class project extends base {
      */
     public function cleanupBuilds() {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-cleanup")){
+            return $this->oUser->showDenied();
+        }
         $sDir = $this->_getBuildDir();
         $aDirlist = array();
         $aDelete = array();
@@ -613,6 +620,9 @@ class project extends base {
      */
     public function cleanupVcsCache($iAge = 0) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-cleanup")){
+            return $this->oUser->showDenied();
+        }
         $this->_initVcs();
         if ($this->_oVcs) {
             if (!method_exists($this->_oVcs, "cleanupCache")) {
@@ -834,6 +844,12 @@ class project extends base {
      * @param type $sPhase  current phase
      */
     public function canAcceptPhase($sPhase = false) {
+        if (!$this->oUser->hasPermission("project-action-accept")
+            && !$this->oUser->hasPermission("project-action-accept-$sPhase")
+        ){
+            // echo $this->oUser->showDenied();
+            return false;
+        }
 
         if (!$sPhase) {
             // for better performance: skip check on overview page
@@ -1221,6 +1237,9 @@ class project extends base {
      */
     public function build($sTmpFile = false) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-build")){
+            return $this->oUser->showDenied();
+        }
         global $aParams;
         $sReturn = false;
 
@@ -1621,6 +1640,11 @@ class project extends base {
      */
     public function deploy($sPhase, $bIgnoreDeploytimes = false) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-deploy")
+            && !$this->oUser->hasPermission("project-action-deploy-$sPhase")
+        ){
+            return $this->oUser->showDenied();
+        }
         $aActionList = array(
             'iActive' => 0,
             'label' => t("deploy"),
@@ -1788,6 +1812,12 @@ class project extends base {
      */
     public function accept($sPhase) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-accept")
+            && !$this->oUser->hasPermission("project-action-accept-$sPhase")
+            ){
+            return $this->oUser->showDenied();
+        }
+
         $sReturn = "<h2>" . t("accept") . " " . $this->getLabel() . " :: $sPhase</h2>";
         $this->_logaction(t('starting') . " accept($sPhase)", __FUNCTION__);
 
@@ -1820,6 +1850,9 @@ class project extends base {
      */
     public function saveConfig($aData = false) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-setup")){
+            return $this->oUser->showDenied();
+        }
         $this->_logaction(t('starting') . " saveConfig(...)", __FUNCTION__);
         if (!$aData) {
             $aData = $_POST;
@@ -1859,6 +1892,9 @@ class project extends base {
      */
     public function create($sId) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-create")){
+            return $this->oUser->showDenied();
+        }
         $this->_logaction(t('starting') . " create($sId)", __FUNCTION__);
         if (!$sId) {
             $sError = t("class-project-error-create-missing-id");
@@ -1924,6 +1960,9 @@ class project extends base {
      */
     public function delete($aOptions = array()) {
         $this->log(__FUNCTION__ . " start");
+        if (!$this->oUser->hasPermission("project-action-delete")){
+            return $this->oUser->showDenied();
+        }
         $sCfgfile = $this->_getConfigFile($this->_aConfig["id"]);
         if (!file_exists($sCfgfile)) {
             return t("class-project-error-delete-project-no-configfile");
@@ -2072,15 +2111,13 @@ class project extends base {
                 'label' => t('setup')
             ),
         );
-        // TODO: remove $sRole and use user roles
         /*
-          $sNeedsRole = (
-          array_key_exists($sFunction, $aLinkdata) && array_key_exists('role', $aLinkdata[$sFunction])
-          ) ? $aLinkdata[$sFunction]['role'] : '';
-          if (!$this->oUser->hasRole($sNeedsRole)){
-          return false;
-          }
-         */
+        if (!$this->oUser->hasRole("project-action-$sFunction")){
+            // $sClass .= ' disabled';
+            // return '<span title="no permission [project-action-'.$sFunction.']">[ ]</span>';
+        } 
+         * 
+         */       
         // fuer wen ist der Link
         $sRole = '';
         $sOnMouseover = '';
@@ -2124,6 +2161,10 @@ class project extends base {
         if ($sVersion) {
             $sLink.="$sVersion/";
         }
+        if (!$this->oUser->hasPermission("project-action-$sFunction")){
+            // $sClass .= ' disabled';
+            return '<span title="no permission [project-action-'.$sFunction.'] for '.$this->oUser->getUsername().'">[ <i class="' . $sIconClass . '"></i> ' . $sLabel . ' ]</span>';
+        }        
 
         return '<a href="' . $sLink . '" ' . $sOnMouseover . ' title="' . $sHint . '" class="btn  btn-default ' . $sClass . '"><i class="' . $sIconClass . '"></i> ' . $sLabel . '</a>';
     }
@@ -2560,6 +2601,9 @@ class project extends base {
      * @return string
      */
     public function renderProjectSetup() {
+        if (!$this->oUser->hasPermission("project-action-setup")){
+            return $this->oUser->showDenied();
+        }
 
         $sMessages = '';
         require_once ("formgen.class.php");
@@ -2823,6 +2867,9 @@ class project extends base {
      */
     public function renderNewProject() {
         global $aParams;
+        if (!$this->oUser->hasPermission("project-action-create")){
+            return $this->oUser->showDenied();
+        }
 
         require_once ("formgen.class.php");
         $i = 0;
diff --git a/public_html/deployment/classes/projectlist.class.php b/public_html/deployment/classes/projectlist.class.php
index 30f68880c0bc380b759afaa71e24229ce44479b5..aaed114de3c4bfa03a30ec53e04bc4190c1eac63 100644
--- a/public_html/deployment/classes/projectlist.class.php
+++ b/public_html/deployment/classes/projectlist.class.php
@@ -27,7 +27,8 @@ class projectlist extends base{
     /**
      * constructor2 called from constructor of base class
      */
-    public function _construct2() {
+    public function __construct() {
+        $this->oUser=new user();
         // define 
     }
 
@@ -50,9 +51,6 @@ class projectlist extends base{
      */
     public function renderOverview() {
         
-        if (!$this->oUser->hasRole("viewProjectOverview")){
-            return $this->oUser->showDenied();
-        }
         $sOut = '';  // table
         $sOut2 = ''; // tiles
         $oPrj = false;
@@ -363,5 +361,3 @@ class projectlist extends base{
     }
 
 }
-
-?>
\ No newline at end of file
diff --git a/public_html/deployment/classes/user.class.php b/public_html/deployment/classes/user.class.php
index 9cbddf405d08ea0a38741e53834a1975a7e8b37e..f412e8edb4fb8878c04842e8a8865dfb00adc3ae 100644
--- a/public_html/deployment/classes/user.class.php
+++ b/public_html/deployment/classes/user.class.php
@@ -8,10 +8,35 @@
  */
 class user {
     
+    /**
+     * login name of the current user
+     * @var string
+     */
     private $_sUsername=false;
+    
+    /**
+     * list of groups of the current user
+     * @var array
+     */
     private $_aUserGroups=array();
-    private $_aUserRoles=array();
-    private $_sLastCheckedRole=false;
+    
+    /**
+     * list of roles based on the groups
+     * @var array
+     */
+    private $_aUserPermmissions=array();
+    
+    /**
+     * list of projects the current user is involved in
+     * @var array
+     */
+    private $_aProjects=array();
+    
+    /**
+     * name of the last checked role
+     * @var string
+     */
+    private $_sLastCheckedPermission=false;
     
     /**
      * init user with optional given user
@@ -21,18 +46,35 @@ class user {
         $this->setUser($sUser);
     }
     
+    
+    // ----------------------------------------------------------------------
+    // private functions
+    // ----------------------------------------------------------------------
+    
+    
     /**
      * detect a user
      * @return type
      */
     private function _autoDetectUser(){
         $sUser=false;
-        if (is_array($_SERVER) && array_key_exists("PHP_AUTH_USER", $_SERVER)){
+        if (is_array($_SESSION) && array_key_exists("PHP_AUTH_USER", $_SESSION)){
+            $sUser=$_SESSION["PHP_AUTH_USER"];
+        }
+        if (!$sUser && is_array($_SERVER) && array_key_exists("PHP_AUTH_USER", $_SERVER)){
             $sUser=$_SERVER["PHP_AUTH_USER"];
         }
         return $sUser;
     }
 
+    // UNUSED SO FAR
+    private function _getUser2Projects(){
+        return require(__DIR__ . '/../../../config/inc_user2projects.php');
+    }
+    
+    private function _getUser2Roles(){
+        return require(__DIR__ . '/../../../config/inc_user2roles.php');
+    }
     /**
      * TODO: reimplement
      * get the user groups of the current user from an internal source.
@@ -43,7 +85,13 @@ class user {
         $aGroups=array();
         if ($this->_sUsername){
             $aGroups[]="authenticated";
-            $aGroups[]=$this->_sUsername;
+            // $aGroups[]='#'.$this->_sUsername;
+            $aUserDefinitions=$this->_getUser2Roles();
+            foreach (array_keys($aUserDefinitions) as $sGroup){
+                if (array_search($this->_sUsername, $aUserDefinitions[$sGroup])!==false){
+                    $aGroups[]=$sGroup;
+                }
+            }
         }
         $this->_aUserGroups=$aGroups;
         return $this->_aUserGroups;
@@ -55,44 +103,111 @@ class user {
      * The function returns a flat aray with names of the roles
      * @return array
      */
-    private function _getUserRoles(){
+    private function _getUserPermission(){
         $aRoles=array();
+        $aRolesDefinitions=require(__DIR__ . '/../../../config/inc_roles.php');
 
         // anonymous roles:
-        // $aRoles[]="view";
-        $aRoles[]="viewProjectOverview";
+        $aRoles=array_merge($aRoles, $aRolesDefinitions['all']);
         
-        if ($this->hasGroup("authenticated")){
-            if ($this->hasGroup("developer")){
-                $aRoles[]="build";
-                /*
-                $aRoles[]="deploy";
-                $aRoles[]="accept";
-                $aRoles[]="setup-project";
-                 * 
-                 */
-            }
-            if ($this->hasGroup("admin")){
-                // $aRoles[]="setup-all";
+        foreach (array_keys($aRolesDefinitions) as $sGroup){
+            if ($this->hasGroup($sGroup)){
+                $aRoles=array_merge($aRoles, $aRolesDefinitions[$sGroup]);
             }
         }
         
-        $this->_aUserRoles=$aRoles;
-        return $this->_aUserRoles;
+        $this->_aUserPermmissions=  array_unique($aRoles);
+        return $this->_aUserPermmissions;
     }
     
+    
+    // ----------------------------------------------------------------------
+    // public ACTIONS
+    // ----------------------------------------------------------------------
+    
+    
     /**
-     * TODO: implement authentication somewhere
-     * set a new authenticated user
-     * @param string $sUser  username
+     * authenticate a user with the configured methods
+     * @global array  $aConfig  global config
+     * @global array  $aParams  params (i.e. GET and POST)
+     * @return boolean
      */
-    public function setUser($sUser=false){
-        if (!$sUser){
-            $sUser=$this->_autoDetectUser();
+    public function authenticate(){
+        global $aConfig, $aParams;
+        
+        if(!array_key_exists('auth', $aConfig) || !count($aConfig['auth']) || !array_key_exists('user', $aParams)){
+            return false;
+        }
+        $sUser=$aParams['user'];
+        $sPassword=array_key_exists('password', $aParams)?$aParams['password']:false;
+
+        foreach (array_keys($aConfig['auth']) as $sAuthMethod){
+            $oUserAuth=false;
+            switch ($sAuthMethod){
+                case 'ldap':
+                    require_once("userauth.ldap.class.php");
+                    $oUserAuth=new userauthLdap($aConfig['auth']['ldap']);
+                    break;
+                // implement other methods here
+                // see userauth.ldap.class.php as simple example
+                    
+                default:
+                    echo 'WARNING: authmethod '.$sAuthMethod.' in your config is not implemented in '.basename(__FILE__).' and is useless so far.<br>';
+            }
+            // if authentication fails then continue and try next method
+            if ($oUserAuth && $oUserAuth->authenticate($sUser, $sPassword)){
+                
+                // set a session - it must correspondent with _autoDetectUser()
+                $_SESSION["PHP_AUTH_USER"]=$sUser;
+                $this->setUser();
+                return true;
+            }
         }
-        $this->_sUsername=$sUser;
+        return false;
+    }
+
+    /**
+     * logoff user
+     * @return boolean
+     */
+    public function logoff(){
+        unset($_SESSION["PHP_AUTH_USER"]);
+        $this->setUser();
+        return true;
+    }
+    
+    /**
+     * set a authenticated user and get its roles
+     */
+    public function setUser(){
+        $this->_sUsername=$this->_autoDetectUser();
         $this->_getUserGroups();
-        $this->_getUserRoles();
+        $this->_getUserPermission();
+    }
+    
+    /**
+     * return html code to display a denied message
+     * @return type
+     */
+    public function showDenied(){
+        return '<div class="alert alert-danger" role="alert">'
+        . t("class-user-error-deny-no-role").'<br>('.$this->_sLastCheckedPermission.')</div><br>'
+        . '<a href="/deployment/all/login/" class="btn btn-primary">'.t('menu-login').'</a>'
+        ;
+    }
+    
+    // ----------------------------------------------------------------------
+    // public GETTER
+    // ----------------------------------------------------------------------
+
+    
+    // UNUSED SO FAR
+    public function getUser2Projects(){
+        return $this->_getUser2Projects();
+    }
+    
+    public function getUser2Roles(){
+        return $this->_getUser2Roles();
     }
 
     /**
@@ -113,8 +228,8 @@ class user {
      * get a flat array with roles of the current user
      * @return string
      */
-    public function getUserRoles(){
-        return $this->_aUserRoles;
+    public function getUserPermission(){
+        return $this->_aUserPermmissions;
     }
 
     /**
@@ -127,20 +242,15 @@ class user {
     }
     /**
      * check if the current user has a given role name
-     * @param string  $sRolename  name of the role to check
+     * @param string  $sPermission  name of the role to check
      * @return type
      */
-    public function hasRole($sRolename){
-        $this->_sLastCheckedRole=$sRolename;
-        return (array_search($sRolename, $this->_aUserRoles)!==false);
+    public function hasPermission($sPermission){
+        $this->_sLastCheckedPermission=$sPermission;
+        $bReturn=array_search($sPermission, $this->_aUserPermmissions)!==false;
+        // $this->log(__FUNCTION__ . "($sRolename) -> " . $bReturn ? 'true' : 'false');
+        return $bReturn;
     }
 
-    /**
-     * return html code to display a denied message
-     * @return type
-     */
-    public function showDenied(){
-        return '<div class="error">'.t("class-user-error-deny-no-role").' ('.$this->_sLastCheckedRole.')</div>';
-    }
     
 }
diff --git a/public_html/deployment/classes/userauth.interface.php b/public_html/deployment/classes/userauth.interface.php
new file mode 100644
index 0000000000000000000000000000000000000000..83a4a3acb9c1e5dab5938aa4ea8a39b66a05a19c
--- /dev/null
+++ b/public_html/deployment/classes/userauth.interface.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * interface for user authentication
+ * @author axel.hahn@iml.unibe.ch
+ */
+interface iUserAuth {
+
+    /**
+     * verify if a given user and password combination is correct
+     */
+    public function authenticate($sUser, $sPassword);
+    
+
+}
\ No newline at end of file
diff --git a/public_html/deployment/classes/userauth.ldap.class.php b/public_html/deployment/classes/userauth.ldap.class.php
new file mode 100644
index 0000000000000000000000000000000000000000..ac3086a23689673d06c2961ef0e51dba50a88933
--- /dev/null
+++ b/public_html/deployment/classes/userauth.ldap.class.php
@@ -0,0 +1,50 @@
+<?php
+
+require_once("userauth.interface.php");
+require_once("ldap.class.php");        
+
+/**
+ * user authentication :: LDAP
+ * implements userauth interface
+ * 
+ * @author hahn
+ */
+class userauthLdap implements iUserAuth {
+
+    /**
+     * object for ldap actions
+     * @var object
+     */
+    private $_oLdap=false;
+    
+    // ----------------------------------------------------------------------
+    // 
+    // ----------------------------------------------------------------------
+    public function __construct() {
+        global $aConfig;
+        $this->_oLdap=new imlldap($aConfig['auth']['ldap']);
+        
+        // first test of ldap connection
+        // $this->_oLdap->debugOn();
+        $this->_oLdap->connect();
+        return true;
+    }
+    
+    public function __destruct() {
+        $this->_oLdap->close();
+    }
+    
+    // ----------------------------------------------------------------------
+    // implementation
+    // ----------------------------------------------------------------------
+    /**
+     * verify if a given user and password combination is correct
+     * @param string   $sUser      username
+     * @param password $sPassword  password
+     * @return boolean
+     */
+    public function authenticate($sUser, $sPassword){
+        return $this->_oLdap->verifyPassword($sUser, $sPassword);
+    }
+
+}
diff --git a/public_html/deployment/inc_functions.php b/public_html/deployment/inc_functions.php
index b051396c506ddfd5021d98ecebc0bd391407fcc3..d3e15460b33ad124ae0d09b29e01e36aade439bf 100644
--- a/public_html/deployment/inc_functions.php
+++ b/public_html/deployment/inc_functions.php
@@ -10,15 +10,57 @@
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
   ###################################################################### */
 
-
-if (!isset($aConfig) || !is_array($aConfig)) {
-    die("FATAL ERROR: \$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script.\n");
-}
-
 global $aParams;
 $aParams = array();
 
 
+// ----------------------------------------------------------------------
+// verifiy config
+// ----------------------------------------------------------------------
+    $aErrors=array();
+    if (!isset($aConfig) || !is_array($aConfig)) {
+        $aErrors[]="* \$aConfig does not exist. The config was not included before including " . __FILE__ . " in the request/ script.\n";
+    } else {
+
+        foreach (array(
+            'appRootDir', 
+            'configDir', 
+            'workDir',
+            'dataDir',
+            'buildDir',
+            'buildDefaultsDir',
+            'packageDir',
+            'archiveDir',
+            ) as $sKey){
+            if (!is_dir($aConfig[$sKey])){
+                $aErrors[]="* \$aConfig['$sKey'] pints to a non existing directory (".$aConfig[$sKey].").\n";
+            } else {
+                if (!is_writable($aConfig[$sKey])){
+                    $aErrors[]="* \$aConfig['$sKey'] = ".$aConfig[$sKey]." is NOT writable.\n";
+                }
+            }
+        }
+    }
+        foreach (array(
+            $aConfig['dataDir'].'/database', 
+            $aConfig['dataDir'].'/projects', 
+            $aConfig['dataDir'].'/sshkeys', 
+            ) as $sDir2Check){
+            if (!is_dir($sDir2Check)){
+                $aErrors[]="* directory not found: $sDir2Check\n";
+            } else {
+                if (!is_writable($sDir2Check)){
+                    $aErrors[]="* $sDir2Check is NOT writable.\n";
+                }
+            }
+        }
+    
+        
+    
+    if (count($aErrors)){
+        die("FATAL ERROR on config.<br>" . implode("<br>\n", $aErrors));
+    }
+
 
 // remark: $_SERVER does not exist in CLI
 if (isset($_SERVER) && is_array($_SERVER) && array_key_exists("REQUEST_URI", $_SERVER)) {
@@ -150,9 +192,11 @@ function getTopArea() {
               </nobr>
               &nbsp;&nbsp;&nbsp;
             </div>
-
             <!-- Collect the nav links, forms, and other content for toggling -->
             <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
+            ';
+    if($oUser->getUsername() || true ){
+        $sReturn.='
               <ul class="nav navbar-nav">
               
                 <li class="dropdown';
@@ -221,6 +265,7 @@ function getTopArea() {
                         }
                     }
                 }
+    }
                     
             $sReturn.='
               </ul>
@@ -230,9 +275,8 @@ function getTopArea() {
                     <li class="dropdown">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"
                         ><span class="glyphicon glyphicon-user"></span> ' . $oUser->getUsername() . ' <b class="caret"></b></a>
-                        <ul class="dropdown-menu" role="menu" style="width: 300px;">
-                            <li>groups: <pre>' . print_r($oUser->getUserGroups(), true) . '</pre></li>
-                            <li>roles: <pre>' . print_r($oUser->getUserRoles(), true) . '</pre></li>
+                        <ul class="dropdown-menu" role="menu">
+                            <li><a href="' . $sBaseUrl . 'all/login/">' . t("menu-login") . '</a></li>
                         </ul>
                     </li>
 
@@ -257,151 +301,12 @@ function getTopArea() {
         </div><div id="header2">';
 
     if (!array_key_exists("prj", $aParams)) {
-        $sReturn.='<img src="' . $sImageBase . $aImages['overview'] . '" id="imgtop" alt="">'
-                . '<h1>' . t("overview-label") . '</h1><span class="description">' . t("overview-hint") . '</span>';
-    } else {
-        if ($aParams["prj"] <> "all") {
-            $oPrj = new project($aParams["prj"]);
-            $sReturn.='<img src="' . $sImageBase . $aImages['project'] . '" id="imgtop" alt="">
-                <h1>' . $oPrj->getLabel() . '</h1><span class="description">' . $oPrj->getDescription() . '</span>';
-            if (array_key_exists("action", $aParams)) {
-                // $sReturn.='<h2>Aktion: '.$aParams["action"].'</h2>';
-            }
-        }
-    }
-    $sReturn.='</div>';
-
-    return $sReturn;
-}
-/**
- * auto generate upper part of the page with header and navigation 
- * @global type $aParams
- * @return type
- */
-function getTopArea__bs3() {
-    global $aParams, $sImageBase, $aImages, $aConfig;
-    $sReturn = '';
-    require_once("./classes/project.class.php");
-    require_once("./classes/user.class.php");
-    $oUser = new user();
-
-    $sMyPhase = "[phase]";
-    $sMyRev = " [no rev] ";
-    $sJsonfile = $_SERVER["DOCUMENT_ROOT"] . "ci-webgui.json";
-    if (file_exists($sJsonfile)) {
-        $aJson = json_decode(file_get_contents($sJsonfile), true);
-        if (array_key_exists("date", $aJson))
-            $sMyRev = $aJson["date"];
-    }
-
-    $sBaseUrl = '/deployment/';
-    $sWikiBaseUrl = 'https://secure.iml.unibe.ch/wiki/doku.php';
-    $sReturn = '
-        <span id="top"></span>
-        <nav class="navbar navbar-default" role="navigation">
-          <div class="container-fluid">
-            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
-              
-              <ul class="nav navbar-nav">
-                <li><span class="brand">IML Deployment<br>GUI</span></li>
-                <li class="dropdown';
-    if (!array_key_exists("prj", $aParams))
-        $sReturn.=' active';
-    $sReturn.='">' . str_replace('</a>', ' <b class="caret"></b></a>', aHome("")) . '
-                    <ul class="dropdown-menu" role="menu">
-                        <li><a href="' . $sBaseUrl . 'all/setup/">' . t("menu-settings") . '</a></li>
-                        <li><a href="' . $sBaseUrl . 'all/setup/actionlog/">' . t("menu-logs") . '</a></li>
-                        <li><a href="' . $sBaseUrl . 'all/setup/new/">' . t("menu-new-project") . '</a></li>
-                    </ul>
-                </li>
-                    
-                <!-- list of projects -->
-                <li class="dropdown">
-                    <a href="#" class="dropdown-toggle" data-toggle="dropdown">' . t("menu-projects") . '<b class="caret"></b></a>
-                    <ul class="dropdown-menu" role="menu">
-                        ';
-    $oPrj1 = new project();
-    foreach ($oPrj1->getProjects() as $sPrj) {
-        $oPrj = new project($sPrj);
-        $sReturn.='<li><a href="' . $sBaseUrl . $sPrj . '/">' . $oPrj->getLabel() . '</a></li>';
-    }
-    $sReturn.='
-                    </ul>
-                </li>';
-    if (array_key_exists("prj", $aParams) && $aParams["prj"] <> "all") {
-        $oPrj = new project($aParams["prj"]);
-        $sReturn.='
-                    <li class="active">' . aPrjHome(" ") . '</li>
-                    ';
-        if (array_key_exists("action", $aParams) and FALSE) {
-            $sReturn.='<li><a href="#">Aktion: ' . $aParams["action"] . '</a></li>';
-        } else {
-            $sReturn.='
-                        <li class="dropdown">
-                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">' . t("menu-project-actions") . '<b class="caret"></b></a>
-                        <ul class="dropdown-menu">
-                            <!--
-                            <li><a href="' . $sBaseUrl . $aParams["prj"] . '/build/">' . t("menu-project-build") . '</a></li>
-                            <li><a href="' . $sBaseUrl . $aParams["prj"] . '/cleanup/">' . t("menu-project-cleanup") . '</a></li>
-                            -->
-                            <li><a href="' . $sBaseUrl . $aParams["prj"] . '/setup/">' . t("menu-project-settings") . '</a></li>
-                            <li><a href="' . $sBaseUrl . $aParams["prj"] . '/delete/">' . t("menu-project-delete") . '</a></li>
-                        ';
-            $sReturn.='</ul></li>';
-
-            $aPhases = $oPrj->getActivePhases();
-            if (count($aPhases)) {
-                $sReturn.='<li class="dropdown">
-                                <a href="#" class="dropdown-toggle" data-toggle="dropdown">' . t("menu-project-phases") . '<b class="caret"></b></a>
-                                <ul class="dropdown-menu">';
-                foreach ($aPhases as $sPhase) {
-                    $sReturn.='<li><a href="' . $sBaseUrl . $aParams["prj"] . '/phase/' . $sPhase . '/">' . $sPhase . '</a></li>';
-                }
-                $sReturn.='</ul></li>';
-            }
-        }
-    }
-
-    $sReturn.='
-                <!--
-                <li>
-                    <a href="#" >Irgend...</a>
-                </li>
-                -->
-            </ul>
-            <ul class="nav navbar-nav navbar-right">
-                    <!-- userdata -->
-                    <li class="dropdown">
-                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">user: ' . $oUser->getUsername() . '<b class="caret"></b></a>
-                        <ul class="dropdown-menu" style="width: 300px;">
-                            <li>groups: <pre>' . print_r($oUser->getUserGroups(), true) . '</pre></li>
-                            <li>roles: <pre>' . print_r($oUser->getUserRoles(), true) . '</pre></li>
-                        </ul>
-                    </li>
-
-
-                    <li class="dropdown">
-                        <a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="glyphicon glyphicon-question-sign"></i> ' . t("menu-help") . '<b class="caret"></b></a>
-                        <ul class="dropdown-menu">
-                            <li><a href="' . $sWikiBaseUrl . '/it/entwicklung/continuous_deployment">WIKI: Übersicht Continous Deployment</a></li>
-                            <li><a href="' . $sWikiBaseUrl . '/it/entwicklung/continuous_deployment#konventionen">WIKI: Konventionen für Entwickler</a></li>
-                            <li><a href="' . $sWikiBaseUrl . '/it/infrastruktur/dienste/puppet/snippets#iml-deployment">WIKI (Admin): Puppet-Snippets für den Sysadmin</a></li>
-                            <li><a href="' . $sWikiBaseUrl . '/it/infrastruktur/dienste/imldeployment">WIKI (Admin): Verzeichnisse und Dateien</a></li>
-                            <li><a href="/deployment/all/doc/">' . t("menu-help-classes") . '</a></li>
-                        </ul>
-                    </li>
-            </ul>
-          </div>          
-          <span class="version ">' . $sMyRev . ' @ ' . php_uname("n") . '</span>
-        </div></nav><div id="header2">';
-
-    if (!array_key_exists("prj", $aParams)) {
-        $sReturn.='<img src="' . $sImageBase . $aImages['overview'] . '" id="imgtop" alt="">'
+        $sReturn.='<!-- <img src="' . $sImageBase . $aImages['overview'] . '" id="imgtop" alt=""> -->'
                 . '<h1>' . t("overview-label") . '</h1><span class="description">' . t("overview-hint") . '</span>';
     } else {
         if ($aParams["prj"] <> "all") {
             $oPrj = new project($aParams["prj"]);
-            $sReturn.='<img src="' . $sImageBase . $aImages['project'] . '" id="imgtop" alt="">
+            $sReturn.='<!-- <img src="' . $sImageBase . $aImages['project'] . '" id="imgtop" alt="">-->
                 <h1>' . $oPrj->getLabel() . '</h1><span class="description">' . $oPrj->getDescription() . '</span>';
             if (array_key_exists("action", $aParams)) {
                 // $sReturn.='<h2>Aktion: '.$aParams["action"].'</h2>';
diff --git a/public_html/deployment/index.php b/public_html/deployment/index.php
index 7b96bad1ff4995a8aa745b5e45da1aab576c6d71..2c3f9044377c9796c49df58e1779dbcd3342308d 100644
--- a/public_html/deployment/index.php
+++ b/public_html/deployment/index.php
@@ -16,9 +16,11 @@
   2013-11-08  Axel <axel.hahn@iml.unibe.ch>
   ###################################################################### */
 
+session_start();
 require_once("./classes/page.class.php");
 require_once("../../config/inc_projects_config.php");
 require_once("./classes/logger.class.php");
+require_once("./classes/user.class.php");
 global $oCLog;
 $oCLog = new logger();
 $oCLog->enableDebugByIp($aConfig['showdebug']['ip']);
@@ -49,33 +51,39 @@ $sHeader = '<style>';
 foreach ($aConfig["phases"] as $sPhase => $aData) {
     $sHeader.=array_key_exists("bgdark", $aData["css"]) ? 'th.' . $sPhase . '{' . $aData["css"]["bgdark"] . '}' : '';
     $sHeader.=array_key_exists("bglight", $aData["css"]) ? 'td.' . $sPhase . ', div.' . $sPhase . '{' . $aData["css"]["bglight"] . '}' : '';
-    $sHeader.=array_key_exists("bgbutton", $aData["css"]) ? 'a.' . $sPhase . '{' . $aData["css"]["bgbutton"] . '}' : '';
+    $sHeader.=array_key_exists("bgbutton", $aData["css"]) ? 'a.' . $sPhase . ',a.' . $sPhase . ':hover{' . $aData["css"]["bgbutton"] . '}' : '';
 }
 $sHeader.='</style>';
 $sTopArea=getTopArea();
 $sTopAction=getAction();
 
 // ------ action 
+$oUser=new user();
+if ($oUser->hasPermission('page_'.$sAction)){
 
-$sActionFile = __DIR__ . '/pages/act_' . $sAction . ".php";
+    $sActionFile = __DIR__ . '/pages/act_' . $sAction . ".php";
 
-$oCLog->add("including $sActionFile");
-ob_start();
-if (!@include($sActionFile)) {
-    include("./pages/error_404.php");
+    $oCLog->add("including $sActionFile");
+    ob_start();
+    if (!@include($sActionFile)) {
+        include("./pages/error_404.php");
+    }
+    $sPhpOut = ob_get_contents();
+    ob_end_clean();
+    $oCLog->add("including done $sActionFile");
+
+    $oCLog->add("adding actionlog.class");
+
+    require_once("./classes/actionlog.class.php");
+    $oLog=new Actionlog($sPrj);
+    $aFilter=array('limit'=>'0, 10');
+    if ($sPrj && $sPrj!="all")$aFilter['project']=$sPrj;
+    $sPhpOut.='<div class="logs">' . $oLog->renderLogs($aFilter).'</div>';
+    $oCLog->add("adding actionlog.class done");
+} else {
+    $sPhpOut.=$oUser->showDenied();
+    // return false;
 }
-$sPhpOut = ob_get_contents();
-ob_end_clean();
-$oCLog->add("including done $sActionFile");
-
-$oCLog->add("adding actionlog.class");
-
-require_once("./classes/actionlog.class.php");
-$oLog=new Actionlog($sPrj);
-$aFilter=array('limit'=>'0, 10');
-if ($sPrj && $sPrj!="all")$aFilter['project']=$sPrj;
-$sPhpOut.='<div class="logs">' . $oLog->renderLogs($aFilter).'</div>';
-$oCLog->add("adding actionlog.class done");
 
 $oCLog->add("Finally: rendering page ...");
 
@@ -102,4 +110,3 @@ $oPage->addJsOnReady('');
 
 $oPage->setContent($sPhpOut);
 echo $oPage->render();
-?>
diff --git a/public_html/deployment/pages/act_login.php b/public_html/deployment/pages/act_login.php
new file mode 100644
index 0000000000000000000000000000000000000000..376c1a32de141e7889d2ac93d51adbdf7258b2de
--- /dev/null
+++ b/public_html/deployment/pages/act_login.php
@@ -0,0 +1,126 @@
+<?php
+
+/* ######################################################################
+
+  IML DEPLOYMENT
+
+  webgui - login
+
+  ---------------------------------------------------------------------
+  2015-04-21  Axel <axel.hahn@iml.unibe.ch>
+  ###################################################################### */
+
+require_once("./inc_functions.php");
+$sOut = '';
+
+require_once("./classes/user.class.php");
+$oUser = new user();
+
+// ----------------------------------------------------------------------
+// actions
+// ----------------------------------------------------------------------
+
+// if logoff was sent ...
+if ($oUser->getUsername() && array_key_exists('logoff', $aParams)) {
+    $oUser->logoff();
+    header('Location: ?');
+}
+
+// if user is logged off and credentials were sent: try to authenticate
+if (!$oUser->getUsername() && array_key_exists('user', $aParams)) {
+    $oUser->authenticate();
+}
+
+    // if user is logged in and credentials were sent: reload to remove post vars
+    if ($oUser->getUsername() && array_key_exists('user', $aParams)) {
+        header('Location: ?');
+    }
+
+// ----------------------------------------------------------------------
+// show infos or login form
+// ----------------------------------------------------------------------
+
+// if user is logged in, then show user infos        
+if ($oUser->getUsername()) {
+    $sGrouplist='';
+    foreach ($oUser->getUserGroups() as $sGroupname){
+        $sGrouplist.='<li>' . $sGroupname . '</li>';
+    }
+    $sOut.='<div style="width: 50%; margin-left: 25%;">'
+            . '<h2>' . t("page-login-info") . '</h2>'
+            . '<p>' 
+                . t("page-login-userloggedin") . '<br><br>'
+                . '<strong>' . $oUser->getUsername() . '</strong><br><br>'
+                . t("page-login-usergroups") . '<br>'
+                . '<ul>'.$sGrouplist . '</ul><br>'
+                . '<pre style="float: left; margin-right: 1em;">roles:<br>' . print_r($oUser->getUserPermission(), true) . '</pre>'
+                . '<div style="clear: both; margin-bottom: 1em;"></div>'
+                . '<a href="?logoff=1" class="btn btn-default"><span class="glyphicon glyphicon-off"></span> ' . t('logoff') . '</a>'
+                // . ' ' . aPrjHome() . ''
+            . '</p>'
+            . '</div>';
+} else {
+    $i = 0;
+    require_once ("./classes/formgen.class.php");
+
+    $aForms = array(
+        'login' => array(
+            'meta' => array(
+                'method' => 'POST',
+                'action' => '?',
+            ),
+            'validate' => array(),
+            'form' => array(
+                'input' . $i++ => array(
+                    'type' => 'text',
+                    'name' => 'user',
+                    'label' => t('page-login-username'),
+                    'required' => 'required',
+                    'validate' => 'isastring',
+                    'size' => 10,
+                    'value' => $aParams['user'],
+                    'placeholder' => t('page-login-username'),
+                ),
+                'input' . $i++ => array(
+                    'type' => 'password',
+                    'name' => 'password',
+                    'label' => t('page-login-password'),
+                    'required' => 'required',
+                    'validate' => 'isastring',
+                    'size' => 10,
+                    'value' => $aParams['password'],
+                    'placeholder' => t('page-login-password'),
+                ),
+                'input' . $i++ => array(
+                    'type' => 'markup',
+                    'value' => '<div style="clear: both; margin-bottom: 3em;"></div>'
+                    . '<div style="text-align: center">'
+                ),
+                'input' . $i++ => array(
+                    'type' => 'submit',
+                    'name' => 'btnsave',
+                    'label' => t("login"),
+                    'value' => '<i class="glyphicon glyphicon-ok"></i> ' . t("login"),
+                ),
+                'input' . $i++ => array(
+                    'type' => 'markup',
+                    'value' => '</div>'
+                ),
+            ),
+        )
+    );
+
+    $oForm = new formgen($aForms);
+    $sOut.='<div style="width: 50%; margin-left: 25%;">'
+            . '<h2>' . t("page-login-info") . '</h2>'
+            . '<p>' . t("page-login-info-introtext") . '</p>';
+    if (array_key_exists('user', $aParams)) {
+        $sOut.='<div class="alert alert-danger" role="alert">'.t('page-login-auth-failed').'</div>';
+    }
+    $sOut.= $oForm->renderHtml("login")
+            . '</div>';
+}
+$sOut.= '<div id="navbuttom">' . aPrjHome() . '</div>';
+
+// -- Ausgabe
+echo $sOut;
diff --git a/public_html/deployment/pages/act_setup.php b/public_html/deployment/pages/act_setup.php
index 2394f6f2c0c161ef153f631221e45e81db50a5a7..215f90832a9d2e5908dad04e7fb4888494bda3d0 100644
--- a/public_html/deployment/pages/act_setup.php
+++ b/public_html/deployment/pages/act_setup.php
@@ -145,6 +145,43 @@ if ($aParams["prj"] == "all") {
             $sOut.=$oPrj->renderNewProject();
         }
         // ------------------------------------------------------------
+        // users and roles
+        // ------------------------------------------------------------
+        /*
+        if ($aParams["par3"]=="users") {
+            $sOut.='<h2>'.t("page-setup-info-users-and-roles").'</h2>'
+                    . '<p>'.t("page-setup-info-users-and-roles-introtext").'</p><hr>';
+            
+            $oUserCfg=new user();
+            $aUser2Roles=$oUserCfg->getUser2Roles();
+            // $aUser2Projects=$oUserCfg->getUser2Projects();
+            
+            $aUsers=array();
+            $sOut.=print_r($aUser2Roles,1).'<br>';
+            $sRoles='';
+            foreach ($aUser2Roles as $sRole=>$aUserlist){
+                $sRoles.='<strong>'.$sRole.'</strong><br>';
+                if (count($aUserlist)){
+                    foreach ($aUserlist as $aUseritem){
+                        $aUsers[$aUseritem]=1;
+                        $sRoles.='<span class="user user-'.md5($aUseritem).'">'.$aUseritem.'</span> | ';
+                    }
+                } else {
+                    $sRoles.='---';
+                }
+                $sRoles.='<br><br>';
+            }
+            
+            $sUsers='';
+            foreach(array_keys($aUsers) as $aUseritem){
+                $sUClass='.user-'.md5($aUseritem);
+                $sUsers.='<a href="#" onclick="$(\''.$sUClass.'\').css(\'background\', \'#fe8\');">'.$aUseritem.'</a>';
+            }
+            $sOut.=$sUsers . $sRoles;
+        }
+         * 
+         */
+        // ------------------------------------------------------------
         // check lang-texts
         // ------------------------------------------------------------
         if ($aParams["par3"]=="checklang") {
@@ -233,4 +270,3 @@ $sOut.= '<div id="navbuttom">' . aPrjHome() . '</div>';
 
 // -- Ausgabe
 echo $sOut;
-?>
diff --git a/public_html/webservice/sws-config.json b/public_html/webservice/sws-config.json
index 0048074041f605b3c320c8f40a4b0b5a27a5ccb3..a7b7e3273c59e06c4baf89e8dfb4962564307f84 100644
--- a/public_html/webservice/sws-config.json
+++ b/public_html/webservice/sws-config.json
@@ -1,6 +1,7 @@
 {
     "options": {
-        "enableGui": 0
+        "enableGui": 1,
+        "enableDump": 1
     },
     "classes": {
         "Actionlog": {