]> git.immae.eu Git - perso/Immae/Config/Nix.git/commitdiff
Add wallabag
authorIsmaël Bouya <ismael.bouya@normalesup.org>
Mon, 21 Jan 2019 02:23:49 +0000 (03:23 +0100)
committerIsmaël Bouya <ismael.bouya@normalesup.org>
Mon, 21 Jan 2019 02:23:49 +0000 (03:23 +0100)
virtual/modules/databases/default.nix
virtual/modules/websites/tools/tools/default.nix
virtual/modules/websites/tools/tools/wallabag.nix [new file with mode: 0644]
virtual/modules/websites/tools/tools/wallabag_ldap.patch [new file with mode: 0644]

index 0e2183704be102a6580769065f4f074529e418f3..b896428c6863edbbee19c40e7b65681ee610ee6b 100644 (file)
@@ -166,6 +166,7 @@ in {
     # Nextcloud: 14
     # Mastodon: 13
     # Mediagoblin: 12
+    # wallabag: 0 ?
     services.redis = rec {
       enable = config.services.myDatabases.redis.enable;
       bind = "127.0.0.1";
index f29ac11097ca158c5b29e6768724420413d12bd0..d69ccc9c6497142a9a44ffa507a694af664a8d3d 100644 (file)
@@ -4,6 +4,7 @@ let
     ympd = pkgs.callPackage ./ympd.nix {};
     ttrss = pkgs.callPackage ./ttrss.nix { inherit (mylibs) checkEnv fetchedGithub fetchedGit; };
     roundcubemail = pkgs.callPackage ./roundcubemail.nix { inherit (mylibs) checkEnv; };
+    wallabag = pkgs.callPackage ./wallabag.nix { inherit (mylibs) checkEnv; };
 
     cfg = config.services.myWebsites.tools.tools;
 in {
@@ -18,7 +19,8 @@ in {
       adminer.apache.modules
       ++ ympd.apache.modules
       ++ ttrss.apache.modules
-      ++ roundcubemail.apache.modules;
+      ++ roundcubemail.apache.modules
+      ++ wallabag.apache.modules;
 
     services.ympd = ympd.config // { enable = false; };
 
@@ -31,6 +33,7 @@ in {
         ympd.apache.vhostConf
         ttrss.apache.vhostConf
         roundcubemail.apache.vhostConf
+        wallabag.apache.vhostConf
       ];
     };
 
@@ -38,11 +41,13 @@ in {
       adminer = adminer.phpFpm.pool;
       ttrss = ttrss.phpFpm.pool;
       roundcubemail = roundcubemail.phpFpm.pool;
+      wallabag = wallabag.phpFpm.pool;
     };
 
     system.activationScripts = {
       ttrss = ttrss.activationScript;
       roundcubemail = roundcubemail.activationScript;
+      wallabag = wallabag.activationScript;
     };
 
     systemd.services.tt-rss = {
diff --git a/virtual/modules/websites/tools/tools/wallabag.nix b/virtual/modules/websites/tools/tools/wallabag.nix
new file mode 100644 (file)
index 0000000..92787b8
--- /dev/null
@@ -0,0 +1,160 @@
+{ stdenv, fetchurl, writeText, checkEnv, phpPackages, php, which }:
+let
+  wallabag = rec {
+    varDir = "/var/lib/wallabag";
+    parameters =
+      assert checkEnv "NIXOPS_WALLABAG_SQL_PASSWORD";
+      assert checkEnv "NIXOPS_WALLABAG_SECRET";
+      assert checkEnv "NIXOPS_WALLABAG_LDAP_PASSWORD";
+    writeText "parameters.yml" ''
+      # This file is auto-generated during the composer install
+      parameters:
+          database_driver: pdo_pgsql
+          database_driver_class: Wallabag\CoreBundle\Doctrine\DBAL\Driver\CustomPostgreSQLDriver
+          database_host: db-1.immae.eu
+          database_port: null
+          database_name: webapps
+          database_user: wallabag
+          database_password: ${builtins.getEnv "NIXOPS_WALLABAG_SQL_PASSWORD"}
+          database_path: null
+          database_table_prefix: wallabag_
+          database_socket: null
+          database_charset: utf8
+          domain_name: https://tools.immae.eu/wallabag
+          mailer_transport: smtp
+          mailer_host: mail.immae.eu
+          mailer_user: null
+          mailer_password: null
+          locale: fr
+          secret: ${builtins.getEnv "NIXOPS_WALLABAG_SECRET"}
+          twofactor_auth: true
+          twofactor_sender: wallabag@immae.eu
+          fosuser_registration: false
+          fosuser_confirmation: true
+          from_email: wallabag@immae.eu
+          rss_limit: 50
+          rabbitmq_host: localhost
+          rabbitmq_port: 5672
+          rabbitmq_user: guest
+          rabbitmq_password: guest
+          rabbitmq_prefetch_count: 10
+          redis_scheme: tcp
+          redis_host: localhost
+          redis_port: 6379
+          redis_path: null
+          redis_password: null
+          sites_credentials: {  }
+          ldap_enabled: true
+          ldap_host: ldap.immae.eu
+          ldap_port: 636
+          ldap_tls: false
+          ldap_ssl: true
+          ldap_bind_requires_dn: true
+          ldap_base: 'dc=immae,dc=eu'
+          ldap_manager_dn: 'cn=wallabag,ou=services,dc=immae,dc=eu'
+          ldap_manager_pw: ${builtins.getEnv "NIXOPS_WALLABAG_LDAP_PASSWORD"}
+          ldap_filter: '(&(memberOf=cn=users,cn=wallabag,ou=services,dc=immae,dc=eu))'
+          ldap_admin_filter: '(&(memberOf=cn=admins,cn=wallabag,ou=services,dc=immae,dc=eu)(uid=%s))'
+          ldap_username_attribute: uid
+          ldap_email_attribute: mail
+          ldap_name_attribute: cn
+          ldap_enabled_attribute: null
+      '';
+    webappDir = stdenv.mkDerivation rec {
+      # Beware when upgrading, I probably messed up with the migrations table
+      # (due to a psql bug in wallabag)
+      version = "2.3.2";
+      name = "wallabag-${version}";
+      src = fetchurl {
+        url = "https://static.wallabag.org/releases/wallabag-release-${version}.tar.gz";
+        sha256 = "17yczdvgl43j6wa7hksxi2b51afvyd56vdya6hbbv68iiba4jyh4";
+      };
+      patches = [ ./wallabag_ldap.patch ];
+      dontBuild = "true";
+      installPhase = ''
+        cp -a . $out
+        cd $out
+        export SYMFONY_ENV=prod
+        php -d memory_limit=-1 ${phpPackages.composer}/libexec/composer/composer.phar require --update-no-dev -o --prefer-dist fr3d/ldap-bundle
+        rm -rf web/assets var/cache app/config/parameters.yml data
+        mv var var_old
+        ln -sf ${parameters} app/config/parameters.yml
+        ln -sf ../../../../../${varDir}/var var
+        ln -sf ../../../../../${varDir}/data data
+        ln -sf ../../../../../../${varDir}/assets web/assets
+      '';
+      buildInputs = [ php phpPackages.composer ];
+    };
+    activationScript = ''
+      install -m 0755 -o ${apache.user} -g ${apache.group} -d ${varDir} \
+        ${varDir}/var ${varDir}/data/db ${varDir}/assets/images
+      if [ ! -f "${varDir}/currentWebappDir" -o \
+          "${webappDir}" != "$(cat ${varDir}/currentWebappDir 2>/dev/null)" ]; then
+        pushd ${webappDir} > /dev/null
+        $wrapperDir/sudo -u wwwrun ./bin/console --env=prod doctrine:migrations:migrate --no-interaction
+        $wrapperDir/sudo -u wwwrun ./bin/console --env=prod cache:clear
+        popd > /dev/null
+        echo -n "${webappDir}" > ${varDir}/currentWebappDir
+      fi
+      '';
+    webRoot = "${webappDir}/web";
+    apache = {
+      user = "wwwrun";
+      group = "wwwrun";
+      modules = [ "proxy_fcgi" ];
+      vhostConf = ''
+        # FIXME
+        Alias /assets "${varDir}/assets"
+        Alias /wallabag "${webRoot}"
+        <Directory "${webRoot}">
+          AllowOverride None
+          Require all granted
+          # For OAuth (apps)
+          CGIPassAuth On
+
+          <FilesMatch "\.php$">
+            SetHandler "proxy:unix:${phpFpm.socket}|fcgi://localhost"
+          </FilesMatch>
+
+          <IfModule mod_rewrite.c>
+            Options -MultiViews
+            RewriteEngine On
+            RewriteCond %{REQUEST_FILENAME} !-f
+            RewriteRule ^(.*)$ app.php [QSA,L]
+          </IfModule>
+        </Directory>
+        <Directory "${webRoot}/bundles">
+          <IfModule mod_rewrite.c>
+            RewriteEngine Off
+          </IfModule>
+        </Directory>
+        <Directory "${varDir}/assets">
+          AllowOverride None
+          Require all granted
+        </Directory>
+        '';
+    };
+    phpFpm = rec {
+      basedir = builtins.concatStringsSep ":" [ webappDir parameters varDir ];
+      socket = "/var/run/phpfpm/wallabag.sock";
+      pool = ''
+        listen = ${socket}
+        user = ${apache.user}
+        group = ${apache.group}
+        listen.owner = ${apache.user}
+        listen.group = ${apache.group}
+        pm = dynamic
+        pm.max_children = 60
+        pm.start_servers = 2
+        pm.min_spare_servers = 1
+        pm.max_spare_servers = 10
+
+        ; Needed to avoid clashes in browser cookies (same domain)
+        php_value[session.name] = WallabagPHPSESSID
+        php_admin_value[open_basedir] = "${basedir}:/tmp"
+        php_value[max_execution_time] = 300
+        '';
+    };
+  };
+in
+  wallabag
diff --git a/virtual/modules/websites/tools/tools/wallabag_ldap.patch b/virtual/modules/websites/tools/tools/wallabag_ldap.patch
new file mode 100644 (file)
index 0000000..1b93b57
--- /dev/null
@@ -0,0 +1,537 @@
+commit 4cd6e7f3bbcff7e2a1c5a39917176d28fa02911f
+Author: Ismaël Bouya <ismael.bouya@normalesup.org>
+Date:   Sat Jun 16 11:40:00 2018 +0200
+
+    Add ldap
+
+diff --git a/.travis.yml b/.travis.yml
+index 57b3aa53..3b7638eb 100644
+--- a/.travis.yml
++++ b/.travis.yml
+@@ -54,6 +54,7 @@ branches:
+ before_script:
+     - PHP=$TRAVIS_PHP_VERSION
++    - echo "extension=ldap.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
+     - if [[ ! $PHP = hhvm* ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
+     # xdebug isn't enable for PHP 7.1
+     - if [[ ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
+diff --git a/app/AppKernel.php b/app/AppKernel.php
+index 40726f05..c4f465dc 100644
+--- a/app/AppKernel.php
++++ b/app/AppKernel.php
+@@ -42,6 +42,10 @@ class AppKernel extends Kernel
+             new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
+         ];
++        if (class_exists('FR3D\\LdapBundle\\FR3DLdapBundle')) {
++          $bundles[] = new FR3D\LdapBundle\FR3DLdapBundle();
++        }
++
+         if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
+             $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
+             $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
+diff --git a/app/DoctrineMigrations/Version20170710113900.php b/app/DoctrineMigrations/Version20170710113900.php
+new file mode 100644
+index 00000000..7be83110
+--- /dev/null
++++ b/app/DoctrineMigrations/Version20170710113900.php
+@@ -0,0 +1,54 @@
++<?php
++
++namespace Application\Migrations;
++
++use Doctrine\DBAL\Migrations\AbstractMigration;
++use Doctrine\DBAL\Schema\Schema;
++use Symfony\Component\DependencyInjection\ContainerAwareInterface;
++use Symfony\Component\DependencyInjection\ContainerInterface;
++
++/**
++ * Added dn field on wallabag_users
++ */
++class Version20170710113900 extends AbstractMigration implements ContainerAwareInterface
++{
++    /**
++     * @var ContainerInterface
++     */
++    private $container;
++
++    public function setContainer(ContainerInterface $container = null)
++    {
++        $this->container = $container;
++    }
++
++    private function getTable($tableName)
++    {
++        return $this->container->getParameter('database_table_prefix').$tableName;
++    }
++
++    /**
++     * @param Schema $schema
++     */
++    public function up(Schema $schema)
++    {
++        $usersTable = $schema->getTable($this->getTable('user'));
++
++        $this->skipIf($usersTable->hasColumn('dn'), 'It seems that you already played this migration.');
++
++        $usersTable->addColumn('dn', 'text', [
++            'default' => null,
++            'notnull' => false,
++        ]);
++    }
++
++    /**
++     * @param Schema $schema
++     */
++    public function down(Schema $schema)
++    {
++        $usersTable = $schema->getTable($this->getTable('user'));
++        $usersTable->dropColumn('dn');
++    }
++}
++
+diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist
+index 6b0cb8e8..cfd41b69 100644
+--- a/app/config/parameters.yml.dist
++++ b/app/config/parameters.yml.dist
+@@ -62,3 +62,23 @@ parameters:
+     redis_port: 6379
+     redis_path: null
+     redis_password: null
++
++    # ldap configuration
++    # To enable, you need to require fr3d/ldap-bundle
++    ldap_enabled: false
++    ldap_host: localhost
++    ldap_port: 389
++    ldap_tls:  false
++    ldap_ssl:  false
++    ldap_bind_requires_dn: true
++    ldap_base: dc=example,dc=com
++    ldap_manager_dn: ou=Manager,dc=example,dc=com
++    ldap_manager_pw: password
++    ldap_filter: (&(ObjectClass=Person))
++    # optional (if null: no ldap user is admin)
++    ldap_admin_filter: (&(memberOf=ou=admins,dc=example,dc=com)(uid=%s))
++    ldap_username_attribute: uid
++    ldap_email_attribute: mail
++    ldap_name_attribute: cn
++    # optional (default sets user as enabled unconditionally)
++    ldap_enabled_attribute: ~
+diff --git a/app/config/security.yml b/app/config/security.yml
+index 796dc361..59f48626 100644
+--- a/app/config/security.yml
++++ b/app/config/security.yml
+@@ -6,6 +6,7 @@ security:
+         ROLE_ADMIN: ROLE_USER
+         ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
++    # /!\ This list is modified in WallabagUserBundle when LDAP is enabled
+     providers:
+         administrators:
+             entity:
+@@ -36,6 +37,7 @@ security:
+             pattern: ^/login$
+             anonymous:  ~
++        # /!\ This section is modified in WallabagUserBundle when LDAP is enabled
+         secured_area:
+             pattern: ^/
+             form_login:
+diff --git a/composer.json b/composer.json
+index dca274ed..f115d229 100644
+--- a/composer.json
++++ b/composer.json
+@@ -87,6 +87,9 @@
+         "defuse/php-encryption": "^2.1",
+         "html2text/html2text": "^4.1"
+     },
++    "suggest": {
++      "fr3d/ldap-bundle": "If you want to authenticate via LDAP"
++    },
+     "require-dev": {
+         "doctrine/doctrine-fixtures-bundle": "~2.2",
+         "doctrine/data-fixtures": "~1.1",
+diff --git a/scripts/install.sh b/scripts/install.sh
+index 62a46f4f..5ea3933c 100644
+--- a/scripts/install.sh
++++ b/scripts/install.sh
+@@ -12,5 +12,8 @@ ENV=$1
+ TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
+ git checkout $TAG
++if [ -n "$LDAP_ENABLED" ]; then
++  SYMFONY_ENV=$ENV $COMPOSER_COMMAND require --no-update fr3d/ldap-bundle
++fi
+ SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
+ php bin/console wallabag:install --env=$ENV
+diff --git a/scripts/update.sh b/scripts/update.sh
+index d0598135..753ccbc3 100644
+--- a/scripts/update.sh
++++ b/scripts/update.sh
+@@ -18,6 +18,9 @@ git fetch origin
+ git fetch --tags
+ TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
+ git checkout $TAG --force
++if [ -n "$LDAP_ENABLED" ]; then
++  SYMFONY_ENV=$ENV $COMPOSER_COMMAND require --no-update fr3d/ldap-bundle
++fi
+ SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
+ php bin/console doctrine:migrations:migrate --no-interaction --env=$ENV
+ php bin/console cache:clear --env=$ENV
+diff --git a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
+index 5ca3482e..904a6af1 100644
+--- a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
++++ b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
+@@ -6,9 +6,34 @@ use Symfony\Component\Config\FileLocator;
+ use Symfony\Component\DependencyInjection\ContainerBuilder;
+ use Symfony\Component\DependencyInjection\Loader;
+ use Symfony\Component\HttpKernel\DependencyInjection\Extension;
++use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
+-class WallabagUserExtension extends Extension
++class WallabagUserExtension extends Extension implements PrependExtensionInterface
+ {
++    public function prepend(ContainerBuilder $container)
++    {
++        $ldap = $container->getParameter('ldap_enabled');
++
++        if ($ldap) {
++            $container->prependExtensionConfig('security', array(
++              'providers' => array(
++                'chain_provider' => array(),
++              ),
++            ));
++            $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
++            $loader->load('ldap.yml');
++        } elseif ($container->hasExtension('fr3d_ldap')) {
++            $container->prependExtensionConfig('fr3_d_ldap', array(
++            'driver' => array(
++              'host' => 'localhost',
++            ),
++            'user' => array(
++              'baseDn' => 'dc=example,dc=com',
++            ),
++          ));
++        }
++    }
++
+     public function load(array $configs, ContainerBuilder $container)
+     {
+         $configuration = new Configuration();
+@@ -16,6 +41,9 @@ class WallabagUserExtension extends Extension
+         $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
+         $loader->load('services.yml');
++        if ($container->getParameter('ldap_enabled')) {
++            $loader->load('ldap_services.yml');
++        }
+         $container->setParameter('wallabag_user.registration_enabled', $config['registration_enabled']);
+     }
+diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
+index 48446e3c..f93c59c7 100644
+--- a/src/Wallabag/UserBundle/Entity/User.php
++++ b/src/Wallabag/UserBundle/Entity/User.php
+@@ -1,5 +1,15 @@
+ <?php
++// This permits to have the LdapUserInterface even when fr3d/ldap-bundle is not
++// in the packages
++namespace FR3D\LdapBundle\Model;
++
++interface LdapUserInterface
++{
++    public function setDn($dn);
++    public function getDn();
++}
++
+ namespace Wallabag\UserBundle\Entity;
+ use Doctrine\Common\Collections\ArrayCollection;
+@@ -16,6 +26,7 @@ use Wallabag\ApiBundle\Entity\Client;
+ use Wallabag\CoreBundle\Entity\Config;
+ use Wallabag\CoreBundle\Entity\Entry;
+ use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
++use FR3D\LdapBundle\Model\LdapUserInterface;
+ /**
+  * User.
+@@ -28,7 +39,7 @@ use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
+  * @UniqueEntity("email")
+  * @UniqueEntity("username")
+  */
+-class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface
++class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface, LdapUserInterface
+ {
+     use EntityTimestampsTrait;
+@@ -67,6 +78,13 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
+      */
+     protected $email;
++    /**
++     * @var string
++     *
++     * @ORM\Column(name="dn", type="text", nullable=true)
++     */
++    protected $dn;
++
+     /**
+      * @var \DateTime
+      *
+@@ -309,4 +327,33 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
+             return $this->clients->first();
+         }
+     }
++
++    /**
++     * Set dn.
++     *
++     * @param string $dn
++     *
++     * @return User
++     */
++    public function setDn($dn)
++    {
++        $this->dn = $dn;
++
++        return $this;
++    }
++
++    /**
++     * Get dn.
++     *
++     * @return string
++     */
++    public function getDn()
++    {
++        return $this->dn;
++    }
++
++    public function isLdapUser()
++    {
++        return $this->dn !== null;
++    }
+ }
+diff --git a/src/Wallabag/UserBundle/LdapHydrator.php b/src/Wallabag/UserBundle/LdapHydrator.php
+new file mode 100644
+index 00000000..cea2450f
+--- /dev/null
++++ b/src/Wallabag/UserBundle/LdapHydrator.php
+@@ -0,0 +1,103 @@
++<?php
++
++namespace Wallabag\UserBundle;
++
++use FR3D\LdapBundle\Hydrator\HydratorInterface;
++use FOS\UserBundle\FOSUserEvents;
++use FOS\UserBundle\Event\UserEvent;
++
++class LdapHydrator implements HydratorInterface
++{
++    private $userManager;
++    private $eventDispatcher;
++    private $attributesMap;
++    private $enabledAttribute;
++    private $ldapBaseDn;
++    private $ldapAdminFilter;
++    private $ldapDriver;
++
++    public function __construct(
++      $user_manager,
++      $event_dispatcher,
++      array $attributes_map,
++      $ldap_base_dn,
++      $ldap_admin_filter,
++      $ldap_driver
++    ) {
++        $this->userManager = $user_manager;
++        $this->eventDispatcher = $event_dispatcher;
++
++        $this->attributesMap = array(
++        'setUsername' => $attributes_map[0],
++        'setEmail' => $attributes_map[1],
++        'setName' => $attributes_map[2],
++      );
++        $this->enabledAttribute = $attributes_map[3];
++
++        $this->ldapBaseDn = $ldap_base_dn;
++        $this->ldapAdminFilter = $ldap_admin_filter;
++        $this->ldapDriver = $ldap_driver;
++    }
++
++    public function hydrate(array $ldapEntry)
++    {
++        $user = $this->userManager->findUserBy(array('dn' => $ldapEntry['dn']));
++
++        if (!$user) {
++            $user = $this->userManager->createUser();
++            $user->setDn($ldapEntry['dn']);
++            $user->setPassword('');
++            $user->setSalt('');
++            $this->updateUserFields($user, $ldapEntry);
++
++            $event = new UserEvent($user);
++            $this->eventDispatcher->dispatch(FOSUserEvents::USER_CREATED, $event);
++
++            $this->userManager->reloadUser($user);
++        } else {
++            $this->updateUserFields($user, $ldapEntry);
++        }
++
++        return $user;
++    }
++
++    private function updateUserFields($user, $ldapEntry)
++    {
++        foreach ($this->attributesMap as $key => $value) {
++            if (is_array($ldapEntry[$value])) {
++                $ldap_value = $ldapEntry[$value][0];
++            } else {
++                $ldap_value = $ldapEntry[$value];
++            }
++
++            call_user_func([$user, $key], $ldap_value);
++        }
++
++        if ($this->enabledAttribute !== null) {
++            $user->setEnabled($ldapEntry[$this->enabledAttribute]);
++        } else {
++            $user->setEnabled(true);
++        }
++
++        if ($this->isAdmin($user)) {
++            $user->addRole('ROLE_SUPER_ADMIN');
++        } else {
++            $user->removeRole('ROLE_SUPER_ADMIN');
++        }
++
++        $this->userManager->updateUser($user, true);
++    }
++
++    private function isAdmin($user)
++    {
++        if ($this->ldapAdminFilter === null) {
++            return false;
++        }
++
++        $escaped_username = ldap_escape($user->getUsername(), '', LDAP_ESCAPE_FILTER);
++        $filter = sprintf($this->ldapAdminFilter, $escaped_username);
++        $entries = $this->ldapDriver->search($this->ldapBaseDn, $filter);
++
++        return $entries['count'] == 1;
++    }
++}
+diff --git a/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php b/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php
+new file mode 100644
+index 00000000..8a851f12
+--- /dev/null
++++ b/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php
+@@ -0,0 +1,43 @@
++<?php
++
++namespace Wallabag\UserBundle;
++
++use FOS\OAuthServerBundle\Storage\OAuthStorage;
++use OAuth2\Model\IOAuth2Client;
++use Symfony\Component\Security\Core\Exception\AuthenticationException;
++
++class OAuthStorageLdapWrapper extends OAuthStorage
++{
++    private $ldapManager;
++
++    public function setLdapManager($ldap_manager)
++    {
++        $this->ldapManager = $ldap_manager;
++    }
++
++    public function checkUserCredentials(IOAuth2Client $client, $username, $password)
++    {
++        try {
++            $user = $this->userProvider->loadUserByUsername($username);
++        } catch (AuthenticationException $e) {
++            return false;
++        }
++
++        if ($user->isLdapUser()) {
++            return $this->checkLdapUserCredentials($user, $password);
++        } else {
++            return parent::checkUserCredentials($client, $username, $password);
++        }
++    }
++
++    private function checkLdapUserCredentials($user, $password)
++    {
++        if ($this->ldapManager->bind($user, $password)) {
++            return array(
++        'data' => $user,
++      );
++        } else {
++            return false;
++        }
++    }
++}
+diff --git a/src/Wallabag/UserBundle/Resources/config/ldap.yml b/src/Wallabag/UserBundle/Resources/config/ldap.yml
+new file mode 100644
+index 00000000..5ec16088
+--- /dev/null
++++ b/src/Wallabag/UserBundle/Resources/config/ldap.yml
+@@ -0,0 +1,28 @@
++fr3d_ldap:
++    service:
++        user_hydrator: ldap_user_hydrator
++    driver:
++        host: "%ldap_host%"
++        port: "%ldap_port%"
++        useSsl: "%ldap_ssl%"
++        useStartTls: "%ldap_tls%"
++        bindRequiresDn: "%ldap_bind_requires_dn%"
++        username: "%ldap_manager_dn%"
++        password: "%ldap_manager_pw%"
++    user:
++        baseDn: "%ldap_base%"
++        filter: "%ldap_filter%"
++        usernameAttribute: "%ldap_username_attribute%"
++security:
++    providers:
++        chain_provider:
++            chain:
++                providers: [ fr3d_ldapbundle, fos_userbundle ]
++        fr3d_ldapbundle:
++            id: fr3d_ldap.security.user.provider
++    firewalls:
++        secured_area:
++            fr3d_ldap: ~
++            form_login:
++                provider: chain_provider
++
+diff --git a/src/Wallabag/UserBundle/Resources/config/ldap_services.yml b/src/Wallabag/UserBundle/Resources/config/ldap_services.yml
+new file mode 100644
+index 00000000..b3e3fd8a
+--- /dev/null
++++ b/src/Wallabag/UserBundle/Resources/config/ldap_services.yml
+@@ -0,0 +1,22 @@
++services:
++    fos_oauth_server.server:
++        class: OAuth2\OAuth2
++        arguments:
++            - "@oauth_storage_ldap_wrapper"
++            - "%fos_oauth_server.server.options%"
++    oauth_storage_ldap_wrapper:
++        class: Wallabag\UserBundle\OAuthStorageLdapWrapper
++        parent: fos_oauth_server.storage
++        calls:
++            - [setLdapManager, ["@fr3d_ldap.ldap_manager"]]
++
++    ldap_user_hydrator:
++        class: Wallabag\UserBundle\LdapHydrator
++        arguments:
++            - "@fos_user.user_manager"
++            - "@event_dispatcher"
++            - [ "%ldap_username_attribute%", "%ldap_email_attribute%", "%ldap_name_attribute%", "%ldap_enabled_attribute%" ]
++            - "%ldap_base%"
++            - "%ldap_admin_filter%"
++            - "@fr3d_ldap.ldap_driver"
++