aboutsummaryrefslogtreecommitdiff
path: root/virtual/modules
diff options
context:
space:
mode:
authorIsmaël Bouya <ismael.bouya@normalesup.org>2019-01-21 03:23:49 +0100
committerIsmaël Bouya <ismael.bouya@normalesup.org>2019-01-21 03:23:49 +0100
commitaebd817b115c1a26a4ec70e5cab9af55ea2c1294 (patch)
treec003cf2062c436633dd427da9420ea7932d0ab23 /virtual/modules
parenta7f7fdae99f7617fb7fdabe1e65423e02a4982b1 (diff)
downloadNix-aebd817b115c1a26a4ec70e5cab9af55ea2c1294.tar.gz
Nix-aebd817b115c1a26a4ec70e5cab9af55ea2c1294.tar.zst
Nix-aebd817b115c1a26a4ec70e5cab9af55ea2c1294.zip
Add wallabag
Diffstat (limited to 'virtual/modules')
-rw-r--r--virtual/modules/databases/default.nix1
-rw-r--r--virtual/modules/websites/tools/tools/default.nix7
-rw-r--r--virtual/modules/websites/tools/tools/wallabag.nix160
-rw-r--r--virtual/modules/websites/tools/tools/wallabag_ldap.patch537
4 files changed, 704 insertions, 1 deletions
diff --git a/virtual/modules/databases/default.nix b/virtual/modules/databases/default.nix
index 0e21837..b896428 100644
--- a/virtual/modules/databases/default.nix
+++ b/virtual/modules/databases/default.nix
@@ -166,6 +166,7 @@ in {
166 # Nextcloud: 14 166 # Nextcloud: 14
167 # Mastodon: 13 167 # Mastodon: 13
168 # Mediagoblin: 12 168 # Mediagoblin: 12
169 # wallabag: 0 ?
169 services.redis = rec { 170 services.redis = rec {
170 enable = config.services.myDatabases.redis.enable; 171 enable = config.services.myDatabases.redis.enable;
171 bind = "127.0.0.1"; 172 bind = "127.0.0.1";
diff --git a/virtual/modules/websites/tools/tools/default.nix b/virtual/modules/websites/tools/tools/default.nix
index f29ac11..d69ccc9 100644
--- a/virtual/modules/websites/tools/tools/default.nix
+++ b/virtual/modules/websites/tools/tools/default.nix
@@ -4,6 +4,7 @@ let
4 ympd = pkgs.callPackage ./ympd.nix {}; 4 ympd = pkgs.callPackage ./ympd.nix {};
5 ttrss = pkgs.callPackage ./ttrss.nix { inherit (mylibs) checkEnv fetchedGithub fetchedGit; }; 5 ttrss = pkgs.callPackage ./ttrss.nix { inherit (mylibs) checkEnv fetchedGithub fetchedGit; };
6 roundcubemail = pkgs.callPackage ./roundcubemail.nix { inherit (mylibs) checkEnv; }; 6 roundcubemail = pkgs.callPackage ./roundcubemail.nix { inherit (mylibs) checkEnv; };
7 wallabag = pkgs.callPackage ./wallabag.nix { inherit (mylibs) checkEnv; };
7 8
8 cfg = config.services.myWebsites.tools.tools; 9 cfg = config.services.myWebsites.tools.tools;
9in { 10in {
@@ -18,7 +19,8 @@ in {
18 adminer.apache.modules 19 adminer.apache.modules
19 ++ ympd.apache.modules 20 ++ ympd.apache.modules
20 ++ ttrss.apache.modules 21 ++ ttrss.apache.modules
21 ++ roundcubemail.apache.modules; 22 ++ roundcubemail.apache.modules
23 ++ wallabag.apache.modules;
22 24
23 services.ympd = ympd.config // { enable = false; }; 25 services.ympd = ympd.config // { enable = false; };
24 26
@@ -31,6 +33,7 @@ in {
31 ympd.apache.vhostConf 33 ympd.apache.vhostConf
32 ttrss.apache.vhostConf 34 ttrss.apache.vhostConf
33 roundcubemail.apache.vhostConf 35 roundcubemail.apache.vhostConf
36 wallabag.apache.vhostConf
34 ]; 37 ];
35 }; 38 };
36 39
@@ -38,11 +41,13 @@ in {
38 adminer = adminer.phpFpm.pool; 41 adminer = adminer.phpFpm.pool;
39 ttrss = ttrss.phpFpm.pool; 42 ttrss = ttrss.phpFpm.pool;
40 roundcubemail = roundcubemail.phpFpm.pool; 43 roundcubemail = roundcubemail.phpFpm.pool;
44 wallabag = wallabag.phpFpm.pool;
41 }; 45 };
42 46
43 system.activationScripts = { 47 system.activationScripts = {
44 ttrss = ttrss.activationScript; 48 ttrss = ttrss.activationScript;
45 roundcubemail = roundcubemail.activationScript; 49 roundcubemail = roundcubemail.activationScript;
50 wallabag = wallabag.activationScript;
46 }; 51 };
47 52
48 systemd.services.tt-rss = { 53 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
index 0000000..92787b8
--- /dev/null
+++ b/virtual/modules/websites/tools/tools/wallabag.nix
@@ -0,0 +1,160 @@
1{ stdenv, fetchurl, writeText, checkEnv, phpPackages, php, which }:
2let
3 wallabag = rec {
4 varDir = "/var/lib/wallabag";
5 parameters =
6 assert checkEnv "NIXOPS_WALLABAG_SQL_PASSWORD";
7 assert checkEnv "NIXOPS_WALLABAG_SECRET";
8 assert checkEnv "NIXOPS_WALLABAG_LDAP_PASSWORD";
9 writeText "parameters.yml" ''
10 # This file is auto-generated during the composer install
11 parameters:
12 database_driver: pdo_pgsql
13 database_driver_class: Wallabag\CoreBundle\Doctrine\DBAL\Driver\CustomPostgreSQLDriver
14 database_host: db-1.immae.eu
15 database_port: null
16 database_name: webapps
17 database_user: wallabag
18 database_password: ${builtins.getEnv "NIXOPS_WALLABAG_SQL_PASSWORD"}
19 database_path: null
20 database_table_prefix: wallabag_
21 database_socket: null
22 database_charset: utf8
23 domain_name: https://tools.immae.eu/wallabag
24 mailer_transport: smtp
25 mailer_host: mail.immae.eu
26 mailer_user: null
27 mailer_password: null
28 locale: fr
29 secret: ${builtins.getEnv "NIXOPS_WALLABAG_SECRET"}
30 twofactor_auth: true
31 twofactor_sender: wallabag@immae.eu
32 fosuser_registration: false
33 fosuser_confirmation: true
34 from_email: wallabag@immae.eu
35 rss_limit: 50
36 rabbitmq_host: localhost
37 rabbitmq_port: 5672
38 rabbitmq_user: guest
39 rabbitmq_password: guest
40 rabbitmq_prefetch_count: 10
41 redis_scheme: tcp
42 redis_host: localhost
43 redis_port: 6379
44 redis_path: null
45 redis_password: null
46 sites_credentials: { }
47 ldap_enabled: true
48 ldap_host: ldap.immae.eu
49 ldap_port: 636
50 ldap_tls: false
51 ldap_ssl: true
52 ldap_bind_requires_dn: true
53 ldap_base: 'dc=immae,dc=eu'
54 ldap_manager_dn: 'cn=wallabag,ou=services,dc=immae,dc=eu'
55 ldap_manager_pw: ${builtins.getEnv "NIXOPS_WALLABAG_LDAP_PASSWORD"}
56 ldap_filter: '(&(memberOf=cn=users,cn=wallabag,ou=services,dc=immae,dc=eu))'
57 ldap_admin_filter: '(&(memberOf=cn=admins,cn=wallabag,ou=services,dc=immae,dc=eu)(uid=%s))'
58 ldap_username_attribute: uid
59 ldap_email_attribute: mail
60 ldap_name_attribute: cn
61 ldap_enabled_attribute: null
62 '';
63 webappDir = stdenv.mkDerivation rec {
64 # Beware when upgrading, I probably messed up with the migrations table
65 # (due to a psql bug in wallabag)
66 version = "2.3.2";
67 name = "wallabag-${version}";
68 src = fetchurl {
69 url = "https://static.wallabag.org/releases/wallabag-release-${version}.tar.gz";
70 sha256 = "17yczdvgl43j6wa7hksxi2b51afvyd56vdya6hbbv68iiba4jyh4";
71 };
72 patches = [ ./wallabag_ldap.patch ];
73 dontBuild = "true";
74 installPhase = ''
75 cp -a . $out
76 cd $out
77 export SYMFONY_ENV=prod
78 php -d memory_limit=-1 ${phpPackages.composer}/libexec/composer/composer.phar require --update-no-dev -o --prefer-dist fr3d/ldap-bundle
79 rm -rf web/assets var/cache app/config/parameters.yml data
80 mv var var_old
81 ln -sf ${parameters} app/config/parameters.yml
82 ln -sf ../../../../../${varDir}/var var
83 ln -sf ../../../../../${varDir}/data data
84 ln -sf ../../../../../../${varDir}/assets web/assets
85 '';
86 buildInputs = [ php phpPackages.composer ];
87 };
88 activationScript = ''
89 install -m 0755 -o ${apache.user} -g ${apache.group} -d ${varDir} \
90 ${varDir}/var ${varDir}/data/db ${varDir}/assets/images
91 if [ ! -f "${varDir}/currentWebappDir" -o \
92 "${webappDir}" != "$(cat ${varDir}/currentWebappDir 2>/dev/null)" ]; then
93 pushd ${webappDir} > /dev/null
94 $wrapperDir/sudo -u wwwrun ./bin/console --env=prod doctrine:migrations:migrate --no-interaction
95 $wrapperDir/sudo -u wwwrun ./bin/console --env=prod cache:clear
96 popd > /dev/null
97 echo -n "${webappDir}" > ${varDir}/currentWebappDir
98 fi
99 '';
100 webRoot = "${webappDir}/web";
101 apache = {
102 user = "wwwrun";
103 group = "wwwrun";
104 modules = [ "proxy_fcgi" ];
105 vhostConf = ''
106 # FIXME
107 Alias /assets "${varDir}/assets"
108 Alias /wallabag "${webRoot}"
109 <Directory "${webRoot}">
110 AllowOverride None
111 Require all granted
112 # For OAuth (apps)
113 CGIPassAuth On
114
115 <FilesMatch "\.php$">
116 SetHandler "proxy:unix:${phpFpm.socket}|fcgi://localhost"
117 </FilesMatch>
118
119 <IfModule mod_rewrite.c>
120 Options -MultiViews
121 RewriteEngine On
122 RewriteCond %{REQUEST_FILENAME} !-f
123 RewriteRule ^(.*)$ app.php [QSA,L]
124 </IfModule>
125 </Directory>
126 <Directory "${webRoot}/bundles">
127 <IfModule mod_rewrite.c>
128 RewriteEngine Off
129 </IfModule>
130 </Directory>
131 <Directory "${varDir}/assets">
132 AllowOverride None
133 Require all granted
134 </Directory>
135 '';
136 };
137 phpFpm = rec {
138 basedir = builtins.concatStringsSep ":" [ webappDir parameters varDir ];
139 socket = "/var/run/phpfpm/wallabag.sock";
140 pool = ''
141 listen = ${socket}
142 user = ${apache.user}
143 group = ${apache.group}
144 listen.owner = ${apache.user}
145 listen.group = ${apache.group}
146 pm = dynamic
147 pm.max_children = 60
148 pm.start_servers = 2
149 pm.min_spare_servers = 1
150 pm.max_spare_servers = 10
151
152 ; Needed to avoid clashes in browser cookies (same domain)
153 php_value[session.name] = WallabagPHPSESSID
154 php_admin_value[open_basedir] = "${basedir}:/tmp"
155 php_value[max_execution_time] = 300
156 '';
157 };
158 };
159in
160 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
index 0000000..1b93b57
--- /dev/null
+++ b/virtual/modules/websites/tools/tools/wallabag_ldap.patch
@@ -0,0 +1,537 @@
1commit 4cd6e7f3bbcff7e2a1c5a39917176d28fa02911f
2Author: Ismaël Bouya <ismael.bouya@normalesup.org>
3Date: Sat Jun 16 11:40:00 2018 +0200
4
5 Add ldap
6
7diff --git a/.travis.yml b/.travis.yml
8index 57b3aa53..3b7638eb 100644
9--- a/.travis.yml
10+++ b/.travis.yml
11@@ -54,6 +54,7 @@ branches:
12
13 before_script:
14 - PHP=$TRAVIS_PHP_VERSION
15+ - echo "extension=ldap.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
16 - if [[ ! $PHP = hhvm* ]]; then echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini; fi;
17 # xdebug isn't enable for PHP 7.1
18 - if [[ ! $PHP = hhvm* ]]; then phpenv config-rm xdebug.ini || echo "xdebug not available"; fi
19diff --git a/app/AppKernel.php b/app/AppKernel.php
20index 40726f05..c4f465dc 100644
21--- a/app/AppKernel.php
22+++ b/app/AppKernel.php
23@@ -42,6 +42,10 @@ class AppKernel extends Kernel
24 new OldSound\RabbitMqBundle\OldSoundRabbitMqBundle(),
25 ];
26
27+ if (class_exists('FR3D\\LdapBundle\\FR3DLdapBundle')) {
28+ $bundles[] = new FR3D\LdapBundle\FR3DLdapBundle();
29+ }
30+
31 if (in_array($this->getEnvironment(), ['dev', 'test'], true)) {
32 $bundles[] = new Symfony\Bundle\DebugBundle\DebugBundle();
33 $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
34diff --git a/app/DoctrineMigrations/Version20170710113900.php b/app/DoctrineMigrations/Version20170710113900.php
35new file mode 100644
36index 00000000..7be83110
37--- /dev/null
38+++ b/app/DoctrineMigrations/Version20170710113900.php
39@@ -0,0 +1,54 @@
40+<?php
41+
42+namespace Application\Migrations;
43+
44+use Doctrine\DBAL\Migrations\AbstractMigration;
45+use Doctrine\DBAL\Schema\Schema;
46+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
47+use Symfony\Component\DependencyInjection\ContainerInterface;
48+
49+/**
50+ * Added dn field on wallabag_users
51+ */
52+class Version20170710113900 extends AbstractMigration implements ContainerAwareInterface
53+{
54+ /**
55+ * @var ContainerInterface
56+ */
57+ private $container;
58+
59+ public function setContainer(ContainerInterface $container = null)
60+ {
61+ $this->container = $container;
62+ }
63+
64+ private function getTable($tableName)
65+ {
66+ return $this->container->getParameter('database_table_prefix').$tableName;
67+ }
68+
69+ /**
70+ * @param Schema $schema
71+ */
72+ public function up(Schema $schema)
73+ {
74+ $usersTable = $schema->getTable($this->getTable('user'));
75+
76+ $this->skipIf($usersTable->hasColumn('dn'), 'It seems that you already played this migration.');
77+
78+ $usersTable->addColumn('dn', 'text', [
79+ 'default' => null,
80+ 'notnull' => false,
81+ ]);
82+ }
83+
84+ /**
85+ * @param Schema $schema
86+ */
87+ public function down(Schema $schema)
88+ {
89+ $usersTable = $schema->getTable($this->getTable('user'));
90+ $usersTable->dropColumn('dn');
91+ }
92+}
93+
94diff --git a/app/config/parameters.yml.dist b/app/config/parameters.yml.dist
95index 6b0cb8e8..cfd41b69 100644
96--- a/app/config/parameters.yml.dist
97+++ b/app/config/parameters.yml.dist
98@@ -62,3 +62,23 @@ parameters:
99 redis_port: 6379
100 redis_path: null
101 redis_password: null
102+
103+ # ldap configuration
104+ # To enable, you need to require fr3d/ldap-bundle
105+ ldap_enabled: false
106+ ldap_host: localhost
107+ ldap_port: 389
108+ ldap_tls: false
109+ ldap_ssl: false
110+ ldap_bind_requires_dn: true
111+ ldap_base: dc=example,dc=com
112+ ldap_manager_dn: ou=Manager,dc=example,dc=com
113+ ldap_manager_pw: password
114+ ldap_filter: (&(ObjectClass=Person))
115+ # optional (if null: no ldap user is admin)
116+ ldap_admin_filter: (&(memberOf=ou=admins,dc=example,dc=com)(uid=%s))
117+ ldap_username_attribute: uid
118+ ldap_email_attribute: mail
119+ ldap_name_attribute: cn
120+ # optional (default sets user as enabled unconditionally)
121+ ldap_enabled_attribute: ~
122diff --git a/app/config/security.yml b/app/config/security.yml
123index 796dc361..59f48626 100644
124--- a/app/config/security.yml
125+++ b/app/config/security.yml
126@@ -6,6 +6,7 @@ security:
127 ROLE_ADMIN: ROLE_USER
128 ROLE_SUPER_ADMIN: [ ROLE_USER, ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH ]
129
130+ # /!\ This list is modified in WallabagUserBundle when LDAP is enabled
131 providers:
132 administrators:
133 entity:
134@@ -36,6 +37,7 @@ security:
135 pattern: ^/login$
136 anonymous: ~
137
138+ # /!\ This section is modified in WallabagUserBundle when LDAP is enabled
139 secured_area:
140 pattern: ^/
141 form_login:
142diff --git a/composer.json b/composer.json
143index dca274ed..f115d229 100644
144--- a/composer.json
145+++ b/composer.json
146@@ -87,6 +87,9 @@
147 "defuse/php-encryption": "^2.1",
148 "html2text/html2text": "^4.1"
149 },
150+ "suggest": {
151+ "fr3d/ldap-bundle": "If you want to authenticate via LDAP"
152+ },
153 "require-dev": {
154 "doctrine/doctrine-fixtures-bundle": "~2.2",
155 "doctrine/data-fixtures": "~1.1",
156diff --git a/scripts/install.sh b/scripts/install.sh
157index 62a46f4f..5ea3933c 100644
158--- a/scripts/install.sh
159+++ b/scripts/install.sh
160@@ -12,5 +12,8 @@ ENV=$1
161 TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
162
163 git checkout $TAG
164+if [ -n "$LDAP_ENABLED" ]; then
165+ SYMFONY_ENV=$ENV $COMPOSER_COMMAND require --no-update fr3d/ldap-bundle
166+fi
167 SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
168 php bin/console wallabag:install --env=$ENV
169diff --git a/scripts/update.sh b/scripts/update.sh
170index d0598135..753ccbc3 100644
171--- a/scripts/update.sh
172+++ b/scripts/update.sh
173@@ -18,6 +18,9 @@ git fetch origin
174 git fetch --tags
175 TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
176 git checkout $TAG --force
177+if [ -n "$LDAP_ENABLED" ]; then
178+ SYMFONY_ENV=$ENV $COMPOSER_COMMAND require --no-update fr3d/ldap-bundle
179+fi
180 SYMFONY_ENV=$ENV $COMPOSER_COMMAND install --no-dev -o --prefer-dist
181 php bin/console doctrine:migrations:migrate --no-interaction --env=$ENV
182 php bin/console cache:clear --env=$ENV
183diff --git a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
184index 5ca3482e..904a6af1 100644
185--- a/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
186+++ b/src/Wallabag/UserBundle/DependencyInjection/WallabagUserExtension.php
187@@ -6,9 +6,34 @@ use Symfony\Component\Config\FileLocator;
188 use Symfony\Component\DependencyInjection\ContainerBuilder;
189 use Symfony\Component\DependencyInjection\Loader;
190 use Symfony\Component\HttpKernel\DependencyInjection\Extension;
191+use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface;
192
193-class WallabagUserExtension extends Extension
194+class WallabagUserExtension extends Extension implements PrependExtensionInterface
195 {
196+ public function prepend(ContainerBuilder $container)
197+ {
198+ $ldap = $container->getParameter('ldap_enabled');
199+
200+ if ($ldap) {
201+ $container->prependExtensionConfig('security', array(
202+ 'providers' => array(
203+ 'chain_provider' => array(),
204+ ),
205+ ));
206+ $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
207+ $loader->load('ldap.yml');
208+ } elseif ($container->hasExtension('fr3d_ldap')) {
209+ $container->prependExtensionConfig('fr3_d_ldap', array(
210+ 'driver' => array(
211+ 'host' => 'localhost',
212+ ),
213+ 'user' => array(
214+ 'baseDn' => 'dc=example,dc=com',
215+ ),
216+ ));
217+ }
218+ }
219+
220 public function load(array $configs, ContainerBuilder $container)
221 {
222 $configuration = new Configuration();
223@@ -16,6 +41,9 @@ class WallabagUserExtension extends Extension
224
225 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
226 $loader->load('services.yml');
227+ if ($container->getParameter('ldap_enabled')) {
228+ $loader->load('ldap_services.yml');
229+ }
230 $container->setParameter('wallabag_user.registration_enabled', $config['registration_enabled']);
231 }
232
233diff --git a/src/Wallabag/UserBundle/Entity/User.php b/src/Wallabag/UserBundle/Entity/User.php
234index 48446e3c..f93c59c7 100644
235--- a/src/Wallabag/UserBundle/Entity/User.php
236+++ b/src/Wallabag/UserBundle/Entity/User.php
237@@ -1,5 +1,15 @@
238 <?php
239
240+// This permits to have the LdapUserInterface even when fr3d/ldap-bundle is not
241+// in the packages
242+namespace FR3D\LdapBundle\Model;
243+
244+interface LdapUserInterface
245+{
246+ public function setDn($dn);
247+ public function getDn();
248+}
249+
250 namespace Wallabag\UserBundle\Entity;
251
252 use Doctrine\Common\Collections\ArrayCollection;
253@@ -16,6 +26,7 @@ use Wallabag\ApiBundle\Entity\Client;
254 use Wallabag\CoreBundle\Entity\Config;
255 use Wallabag\CoreBundle\Entity\Entry;
256 use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
257+use FR3D\LdapBundle\Model\LdapUserInterface;
258
259 /**
260 * User.
261@@ -28,7 +39,7 @@ use Wallabag\CoreBundle\Helper\EntityTimestampsTrait;
262 * @UniqueEntity("email")
263 * @UniqueEntity("username")
264 */
265-class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface
266+class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterface, LdapUserInterface
267 {
268 use EntityTimestampsTrait;
269
270@@ -67,6 +78,13 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
271 */
272 protected $email;
273
274+ /**
275+ * @var string
276+ *
277+ * @ORM\Column(name="dn", type="text", nullable=true)
278+ */
279+ protected $dn;
280+
281 /**
282 * @var \DateTime
283 *
284@@ -309,4 +327,33 @@ class User extends BaseUser implements TwoFactorInterface, TrustedComputerInterf
285 return $this->clients->first();
286 }
287 }
288+
289+ /**
290+ * Set dn.
291+ *
292+ * @param string $dn
293+ *
294+ * @return User
295+ */
296+ public function setDn($dn)
297+ {
298+ $this->dn = $dn;
299+
300+ return $this;
301+ }
302+
303+ /**
304+ * Get dn.
305+ *
306+ * @return string
307+ */
308+ public function getDn()
309+ {
310+ return $this->dn;
311+ }
312+
313+ public function isLdapUser()
314+ {
315+ return $this->dn !== null;
316+ }
317 }
318diff --git a/src/Wallabag/UserBundle/LdapHydrator.php b/src/Wallabag/UserBundle/LdapHydrator.php
319new file mode 100644
320index 00000000..cea2450f
321--- /dev/null
322+++ b/src/Wallabag/UserBundle/LdapHydrator.php
323@@ -0,0 +1,103 @@
324+<?php
325+
326+namespace Wallabag\UserBundle;
327+
328+use FR3D\LdapBundle\Hydrator\HydratorInterface;
329+use FOS\UserBundle\FOSUserEvents;
330+use FOS\UserBundle\Event\UserEvent;
331+
332+class LdapHydrator implements HydratorInterface
333+{
334+ private $userManager;
335+ private $eventDispatcher;
336+ private $attributesMap;
337+ private $enabledAttribute;
338+ private $ldapBaseDn;
339+ private $ldapAdminFilter;
340+ private $ldapDriver;
341+
342+ public function __construct(
343+ $user_manager,
344+ $event_dispatcher,
345+ array $attributes_map,
346+ $ldap_base_dn,
347+ $ldap_admin_filter,
348+ $ldap_driver
349+ ) {
350+ $this->userManager = $user_manager;
351+ $this->eventDispatcher = $event_dispatcher;
352+
353+ $this->attributesMap = array(
354+ 'setUsername' => $attributes_map[0],
355+ 'setEmail' => $attributes_map[1],
356+ 'setName' => $attributes_map[2],
357+ );
358+ $this->enabledAttribute = $attributes_map[3];
359+
360+ $this->ldapBaseDn = $ldap_base_dn;
361+ $this->ldapAdminFilter = $ldap_admin_filter;
362+ $this->ldapDriver = $ldap_driver;
363+ }
364+
365+ public function hydrate(array $ldapEntry)
366+ {
367+ $user = $this->userManager->findUserBy(array('dn' => $ldapEntry['dn']));
368+
369+ if (!$user) {
370+ $user = $this->userManager->createUser();
371+ $user->setDn($ldapEntry['dn']);
372+ $user->setPassword('');
373+ $user->setSalt('');
374+ $this->updateUserFields($user, $ldapEntry);
375+
376+ $event = new UserEvent($user);
377+ $this->eventDispatcher->dispatch(FOSUserEvents::USER_CREATED, $event);
378+
379+ $this->userManager->reloadUser($user);
380+ } else {
381+ $this->updateUserFields($user, $ldapEntry);
382+ }
383+
384+ return $user;
385+ }
386+
387+ private function updateUserFields($user, $ldapEntry)
388+ {
389+ foreach ($this->attributesMap as $key => $value) {
390+ if (is_array($ldapEntry[$value])) {
391+ $ldap_value = $ldapEntry[$value][0];
392+ } else {
393+ $ldap_value = $ldapEntry[$value];
394+ }
395+
396+ call_user_func([$user, $key], $ldap_value);
397+ }
398+
399+ if ($this->enabledAttribute !== null) {
400+ $user->setEnabled($ldapEntry[$this->enabledAttribute]);
401+ } else {
402+ $user->setEnabled(true);
403+ }
404+
405+ if ($this->isAdmin($user)) {
406+ $user->addRole('ROLE_SUPER_ADMIN');
407+ } else {
408+ $user->removeRole('ROLE_SUPER_ADMIN');
409+ }
410+
411+ $this->userManager->updateUser($user, true);
412+ }
413+
414+ private function isAdmin($user)
415+ {
416+ if ($this->ldapAdminFilter === null) {
417+ return false;
418+ }
419+
420+ $escaped_username = ldap_escape($user->getUsername(), '', LDAP_ESCAPE_FILTER);
421+ $filter = sprintf($this->ldapAdminFilter, $escaped_username);
422+ $entries = $this->ldapDriver->search($this->ldapBaseDn, $filter);
423+
424+ return $entries['count'] == 1;
425+ }
426+}
427diff --git a/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php b/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php
428new file mode 100644
429index 00000000..8a851f12
430--- /dev/null
431+++ b/src/Wallabag/UserBundle/OAuthStorageLdapWrapper.php
432@@ -0,0 +1,43 @@
433+<?php
434+
435+namespace Wallabag\UserBundle;
436+
437+use FOS\OAuthServerBundle\Storage\OAuthStorage;
438+use OAuth2\Model\IOAuth2Client;
439+use Symfony\Component\Security\Core\Exception\AuthenticationException;
440+
441+class OAuthStorageLdapWrapper extends OAuthStorage
442+{
443+ private $ldapManager;
444+
445+ public function setLdapManager($ldap_manager)
446+ {
447+ $this->ldapManager = $ldap_manager;
448+ }
449+
450+ public function checkUserCredentials(IOAuth2Client $client, $username, $password)
451+ {
452+ try {
453+ $user = $this->userProvider->loadUserByUsername($username);
454+ } catch (AuthenticationException $e) {
455+ return false;
456+ }
457+
458+ if ($user->isLdapUser()) {
459+ return $this->checkLdapUserCredentials($user, $password);
460+ } else {
461+ return parent::checkUserCredentials($client, $username, $password);
462+ }
463+ }
464+
465+ private function checkLdapUserCredentials($user, $password)
466+ {
467+ if ($this->ldapManager->bind($user, $password)) {
468+ return array(
469+ 'data' => $user,
470+ );
471+ } else {
472+ return false;
473+ }
474+ }
475+}
476diff --git a/src/Wallabag/UserBundle/Resources/config/ldap.yml b/src/Wallabag/UserBundle/Resources/config/ldap.yml
477new file mode 100644
478index 00000000..5ec16088
479--- /dev/null
480+++ b/src/Wallabag/UserBundle/Resources/config/ldap.yml
481@@ -0,0 +1,28 @@
482+fr3d_ldap:
483+ service:
484+ user_hydrator: ldap_user_hydrator
485+ driver:
486+ host: "%ldap_host%"
487+ port: "%ldap_port%"
488+ useSsl: "%ldap_ssl%"
489+ useStartTls: "%ldap_tls%"
490+ bindRequiresDn: "%ldap_bind_requires_dn%"
491+ username: "%ldap_manager_dn%"
492+ password: "%ldap_manager_pw%"
493+ user:
494+ baseDn: "%ldap_base%"
495+ filter: "%ldap_filter%"
496+ usernameAttribute: "%ldap_username_attribute%"
497+security:
498+ providers:
499+ chain_provider:
500+ chain:
501+ providers: [ fr3d_ldapbundle, fos_userbundle ]
502+ fr3d_ldapbundle:
503+ id: fr3d_ldap.security.user.provider
504+ firewalls:
505+ secured_area:
506+ fr3d_ldap: ~
507+ form_login:
508+ provider: chain_provider
509+
510diff --git a/src/Wallabag/UserBundle/Resources/config/ldap_services.yml b/src/Wallabag/UserBundle/Resources/config/ldap_services.yml
511new file mode 100644
512index 00000000..b3e3fd8a
513--- /dev/null
514+++ b/src/Wallabag/UserBundle/Resources/config/ldap_services.yml
515@@ -0,0 +1,22 @@
516+services:
517+ fos_oauth_server.server:
518+ class: OAuth2\OAuth2
519+ arguments:
520+ - "@oauth_storage_ldap_wrapper"
521+ - "%fos_oauth_server.server.options%"
522+ oauth_storage_ldap_wrapper:
523+ class: Wallabag\UserBundle\OAuthStorageLdapWrapper
524+ parent: fos_oauth_server.storage
525+ calls:
526+ - [setLdapManager, ["@fr3d_ldap.ldap_manager"]]
527+
528+ ldap_user_hydrator:
529+ class: Wallabag\UserBundle\LdapHydrator
530+ arguments:
531+ - "@fos_user.user_manager"
532+ - "@event_dispatcher"
533+ - [ "%ldap_username_attribute%", "%ldap_email_attribute%", "%ldap_name_attribute%", "%ldap_enabled_attribute%" ]
534+ - "%ldap_base%"
535+ - "%ldap_admin_filter%"
536+ - "@fr3d_ldap.ldap_driver"
537+