new Wallabag\ApiBundle\WallabagApiBundle(),
new Bazinga\Bundle\HateoasBundle\BazingaHateoasBundle(),
new Lexik\Bundle\FormFilterBundle\LexikFormFilterBundle(),
+ new FOS\OAuthServerBundle\FOSOAuthServerBundle(),
+ new Wallabag\UserBundle\WallabagUserBundle(),
);
if (in_array($this->getEnvironment(), array('dev', 'test'))) {
routing_loader:
default_format: json
-nelmio_api_doc: ~
+nelmio_api_doc:
+ sandbox:
+ enabled: false
nelmio_cors:
defaults:
fos_user:
db_driver: orm
firewall_name: main
- user_class: Wallabag\CoreBundle\Entity\User
+ user_class: Wallabag\UserBundle\Entity\User
+ registration:
+ confirmation:
+ enabled: true
+
+fos_oauth_server:
+ db_driver: orm
+ client_class: Wallabag\ApiBundle\Entity\Client
+ access_token_class: Wallabag\ApiBundle\Entity\AccessToken
+ refresh_token_class: Wallabag\ApiBundle\Entity\RefreshToken
+ auth_code_class: Wallabag\ApiBundle\Entity\AuthCode
+ service:
+ user_provider: fos_user.user_manager
type: fingers_crossed
action_level: error
handler: nested
- wsse:
- type: stream
- path: %kernel.logs_dir%/%kernel.environment%.wsse.log
- level: error
- channels: [wsse]
nested:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.log"
resource: "@NelmioApiDocBundle/Resources/config/routing.yml"
prefix: /api/doc
-login:
- pattern: /login
- defaults: { _controller: WallabagCoreBundle:Security:login }
-
-login_check:
- pattern: /login_check
-
-logout:
- path: /logout
-
rest :
type : rest
resource : "routing_rest.yml"
defaults: { _controller: WallabagCoreBundle:Entry:showUnread, page : 1 }
requirements:
page: \d+
+
+fos_user:
+ resource: "@FOSUserBundle/Resources/config/routing/all.xml"
+
+fos_oauth_server_token:
+ resource: "@FOSOAuthServerBundle/Resources/config/routing/token.xml"
security:
encoders:
- Wallabag\CoreBundle\Entity\User:
- algorithm: sha1
- encode_as_base64: false
- iterations: 1
+ FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
providers:
administrators:
- entity: { class: WallabagCoreBundle:User, property: username }
+ entity: { class: WallabagUserBundle:User, property: username }
fos_userbundle:
id: fos_user.user_provider.username
# the main part of the security, where you can set up firewalls
# for specific sections of your app
firewalls:
- wsse_secured:
- pattern: /api/.*
- wsse: true
- stateless: true
- anonymous: true
+ oauth_token:
+ pattern: ^/oauth/v2/token
+ security: false
+
+ api:
+ pattern: /api/.*
+ fos_oauth: true
+ stateless: true
+ anonymous: true
+
login_firewall:
pattern: ^/login$
anonymous: ~
target: /
access_control:
- - { path: ^/api/salt, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api/doc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- - { path: ^/forgot-password, roles: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
+ - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: /(unread|starred|archive).xml$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
-# Learn more about services, parameters and containers at
-# http://symfony.com/doc/current/book/service_container.html
parameters:
- security.authentication.provider.dao.class: Wallabag\CoreBundle\Security\Authentication\Provider\WallabagAuthenticationProvider
- security.encoder.digest.class: Wallabag\CoreBundle\Security\Authentication\Encoder\WallabagPasswordEncoder
- security.validator.user_password.class: Wallabag\CoreBundle\Security\Validator\WallabagUserPasswordValidator
lexik_form_filter.get_filter.doctrine_orm.class: Wallabag\CoreBundle\Event\Subscriber\CustomDoctrineORMSubscriber
services:
"pagerfanta/pagerfanta": "~1.0.3",
"lexik/form-filter-bundle": "~4.0",
"j0k3r/graby": "~1.0",
- "friendsofsymfony/user-bundle": "dev-master"
+ "friendsofsymfony/user-bundle": "dev-master",
+ "friendsofsymfony/oauth-server-bundle": "^1.4@dev"
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "~2.2.0",
{
"_readme": [
"This file locks the dependencies of your project to a known state",
- "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "350d05d95be50b6d93e8a046f784e00c",
+ "hash": "7c1f2c88df608eb6e1b4bc7c5ed24acc",
"packages": [
{
"name": "doctrine/annotations",
],
"time": "2014-05-20 12:10:12"
},
+ {
+ "name": "friendsofsymfony/oauth-server-bundle",
+ "version": "1.4.2",
+ "target-dir": "FOS/OAuthServerBundle",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfSymfony/FOSOAuthServerBundle.git",
+ "reference": "9e15c229eff547443d686445d629e9356ab0672e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfSymfony/FOSOAuthServerBundle/zipball/9e15c229eff547443d686445d629e9356ab0672e",
+ "reference": "9e15c229eff547443d686445d629e9356ab0672e",
+ "shasum": ""
+ },
+ "require": {
+ "friendsofsymfony/oauth2-php": "~1.1.0",
+ "php": ">=5.3.3",
+ "symfony/framework-bundle": "~2.1",
+ "symfony/security-bundle": "~2.1"
+ },
+ "require-dev": {
+ "doctrine/doctrine-bundle": "~1.0",
+ "doctrine/mongodb-odm": "1.0.*@dev",
+ "doctrine/orm": ">=2.2,<2.5-dev",
+ "symfony/class-loader": "~2.1",
+ "symfony/yaml": "~2.1",
+ "willdurand/propel-typehintable-behavior": "1.0.*"
+ },
+ "suggest": {
+ "doctrine/doctrine-bundle": "*",
+ "doctrine/mongodb-odm-bundle": "*",
+ "propel/propel-bundle": "If you want to use Propel with Symfony2, then you will have to install the PropelBundle",
+ "willdurand/propel-typehintable-behavior": "The Typehintable behavior is useful to add type hints on generated methods, to be compliant with interfaces"
+ },
+ "type": "symfony-bundle",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.4-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "FOS\\OAuthServerBundle": ""
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Arnaud Le Blanc",
+ "email": "arnaud.lb@gmail.com"
+ },
+ {
+ "name": "FriendsOfSymfony Community",
+ "homepage": "https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/contributors"
+ }
+ ],
+ "description": "Symfony2 OAuth Server Bundle",
+ "homepage": "http://friendsofsymfony.github.com",
+ "keywords": [
+ "oauth",
+ "oauth2",
+ "server"
+ ],
+ "time": "2014-10-31 13:44:14"
+ },
+ {
+ "name": "friendsofsymfony/oauth2-php",
+ "version": "1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/FriendsOfSymfony/oauth2-php.git",
+ "reference": "23e76537c4a02e666ab4ba5abe67a69a886a0310"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/FriendsOfSymfony/oauth2-php/zipball/23e76537c4a02e666ab4ba5abe67a69a886a0310",
+ "reference": "23e76537c4a02e666ab4ba5abe67a69a886a0310",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.2",
+ "symfony/http-foundation": "~2.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "OAuth2\\": "lib/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Arnaud Le Blanc",
+ "email": "arnaud.lb@gmail.com"
+ },
+ {
+ "name": "FriendsOfSymfony Community",
+ "homepage": "https://github.com/FriendsOfSymfony/oauth2-php/contributors"
+ }
+ ],
+ "description": "OAuth2 library",
+ "homepage": "https://github.com/FriendsOfSymfony/oauth2-php",
+ "keywords": [
+ "oauth",
+ "oauth2"
+ ],
+ "time": "2014-11-03 10:21:20"
+ },
{
"name": "friendsofsymfony/rest-bundle",
"version": "1.7.1",
"version": "v2.7.0",
"source": {
"type": "git",
- "url": "https://github.com/symfony/AsseticBundle.git",
+ "url": "https://github.com/symfony/assetic-bundle.git",
"reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/AsseticBundle/zipball/3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5",
+ "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5",
"reference": "3ae5c8ca3079b6e0033cc9fbfb6500e2bc964da5",
"shasum": ""
},
"version": "v2.7.1",
"source": {
"type": "git",
- "url": "https://github.com/symfony/MonologBundle.git",
+ "url": "https://github.com/symfony/monolog-bundle.git",
"reference": "9320b6863404c70ebe111e9040dab96f251de7ac"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/MonologBundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac",
+ "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/9320b6863404c70ebe111e9040dab96f251de7ac",
"reference": "9320b6863404c70ebe111e9040dab96f251de7ac",
"shasum": ""
},
"version": "v2.3.8",
"source": {
"type": "git",
- "url": "https://github.com/symfony/SwiftmailerBundle.git",
+ "url": "https://github.com/symfony/swiftmailer-bundle.git",
"reference": "970b13d01871207e81d17b17ddda025e7e21e797"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/SwiftmailerBundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797",
+ "url": "https://api.github.com/repos/symfony/swiftmailer-bundle/zipball/970b13d01871207e81d17b17ddda025e7e21e797",
"reference": "970b13d01871207e81d17b17ddda025e7e21e797",
"shasum": ""
},
},
{
"name": "symfony/symfony",
- "version": "v2.7.5",
+ "version": "v2.7.4",
"source": {
"type": "git",
"url": "https://github.com/symfony/symfony.git",
- "reference": "619528a274647cffc1792063c3ea04c4fa8266a0"
+ "reference": "1fdf23fe28876844b887b0e1935c9adda43ee645"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/symfony/zipball/619528a274647cffc1792063c3ea04c4fa8266a0",
- "reference": "619528a274647cffc1792063c3ea04c4fa8266a0",
+ "url": "https://api.github.com/repos/symfony/symfony/zipball/1fdf23fe28876844b887b0e1935c9adda43ee645",
+ "reference": "1fdf23fe28876844b887b0e1935c9adda43ee645",
"shasum": ""
},
"require": {
- "doctrine/common": "~2.4",
+ "doctrine/common": "~2.3",
"php": ">=5.3.9",
"psr/log": "~1.0",
"twig/twig": "~1.20|~2.0"
},
"require-dev": {
"doctrine/data-fixtures": "1.0.*",
- "doctrine/dbal": "~2.4",
+ "doctrine/dbal": "~2.2",
"doctrine/doctrine-bundle": "~1.2",
- "doctrine/orm": "~2.4,>=2.4.5",
+ "doctrine/orm": "~2.2,>=2.2.3",
"egulias/email-validator": "~1.2",
"ircmaxell/password-compat": "~1.0",
"monolog/monolog": "~1.11",
"keywords": [
"framework"
],
- "time": "2015-09-25 11:16:52"
+ "time": "2015-09-08 14:26:39"
},
{
"name": "tecnickcom/tcpdf",
"aliases": [],
"minimum-stability": "dev",
"stability-flags": {
- "friendsofsymfony/user-bundle": 20
+ "friendsofsymfony/user-bundle": 20,
+ "friendsofsymfony/oauth-server-bundle": 20
},
"prefer-stable": true,
"prefer-lowest": false,
set :application, 'wallabag'
set :repo_url, 'git@github.com:wallabag/wallabag.git'
-set :ssh_user, 'ssh_user'
-server 'server_ip', user: fetch(:ssh_user), roles: %w{web app db}
+set :ssh_user, 'framasoft_bag'
+server '78.46.248.87', user: fetch(:ssh_user), roles: %w{web app db}
set :scm, :git
set :branch, 'v2'
-set :deploy_to, '/var/www/'
+set :deploy_to, '/var/www/v2.wallabag.org/web/'
namespace Wallabag\ApiBundle\Controller;
+use FOS\RestBundle\Controller\FOSRestController;
use Nelmio\ApiDocBundle\Annotation\ApiDoc;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Wallabag\CoreBundle\Entity\Entry;
use Hateoas\Configuration\Route;
use Hateoas\Representation\Factory\PagerfantaFactory;
-class WallabagRestController extends Controller
+class WallabagRestController extends FOSRestController
{
/**
* @param Entry $entry
}
}
- /**
- * Retrieve salt for a giver user.
- *
- * @ApiDoc(
- * parameters={
- * {"name"="username", "dataType"="string", "required"=true, "description"="username"}
- * }
- * )
- *
- * @return array
- */
- public function getSaltAction($username)
+ private function validateAuthentication()
{
- $user = $this
- ->getDoctrine()
- ->getRepository('WallabagCoreBundle:User')
- ->findOneByUsername($username);
-
- if (is_null($user)) {
- throw $this->createNotFoundException();
+ if (false === $this->get('security.context')->isGranted('IS_AUTHENTICATED_FULLY')) {
+ throw new AccessDeniedException();
}
-
- return array($user->getSalt() ?: null);
}
/**
*/
public function getEntriesAction(Request $request)
{
+ $this->validateAuthentication();
+
$isArchived = $request->query->get('archive');
$isStarred = $request->query->get('star');
$sort = $request->query->get('sort', 'created');
*/
public function getEntryAction(Entry $entry)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry, 'json');
*/
public function postEntriesAction(Request $request)
{
+ $this->validateAuthentication();
+
$url = $request->request->get('url');
$entry = $this->get('wallabag_core.content_proxy')->updateEntry(
*/
public function patchEntriesAction(Entry $entry, Request $request)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$title = $request->request->get('title');
$isArchived = $request->request->get('is_archived');
*/
public function deleteEntriesAction(Entry $entry)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($entry);
*/
public function getEntriesTagsAction(Entry $entry)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$json = $this->get('serializer')->serialize($entry->getTags(), 'json');
*/
public function postEntriesTagsAction(Request $request, Entry $entry)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$tags = $request->request->get('tags', '');
if (!empty($tags)) {
*/
public function deleteEntriesTagsAction(Entry $entry, Tag $tag)
{
- $this->validateUserAccess($entry->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($entry->getUser()->getId());
$entry->removeTag($tag);
$em = $this->getDoctrine()->getManager();
*/
public function getTagsAction()
{
+ $this->validateAuthentication();
$json = $this->get('serializer')->serialize($this->getUser()->getTags(), 'json');
return $this->renderJsonResponse($json);
*/
public function deleteTagAction(Tag $tag)
{
- $this->validateUserAccess($tag->getUser()->getId(), $this->getUser()->getId());
+ $this->validateAuthentication();
+ $this->validateUserAccess($tag->getUser()->getId());
$em = $this->getDoctrine()->getManager();
$em->remove($tag);
* If not, throw exception. It means a user try to access information from an other user.
*
* @param int $requestUserId User id from the requested source
- * @param int $currentUserId User id from the retrieved source
*/
- private function validateUserAccess($requestUserId, $currentUserId)
+ private function validateUserAccess($requestUserId)
{
- if ($requestUserId != $currentUserId) {
- throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$currentUserId);
+ $user = $this->get('security.context')->getToken()->getUser();
+ if ($requestUserId != $user->getId()) {
+ throw $this->createAccessDeniedException('Access forbidden. Entry user id: '.$requestUserId.', logged user id: '.$user->getId());
}
}
+++ /dev/null
-<?php
-
-namespace Wallabag\ApiBundle\DependencyInjection\Security\Factory;
-
-use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\DefinitionDecorator;
-use Symfony\Component\Config\Definition\Builder\NodeDefinition;
-use Symfony\Bundle\SecurityBundle\DependencyInjection\Security\Factory\SecurityFactoryInterface;
-
-class WsseFactory implements SecurityFactoryInterface
-{
- public function create(ContainerBuilder $container, $id, $config, $userProvider, $defaultEntryPoint)
- {
- $providerId = 'security.authentication.provider.wsse.'.$id;
- $container
- ->setDefinition($providerId, new DefinitionDecorator('wsse.security.authentication.provider'))
- ->replaceArgument(0, new Reference($userProvider))
- ;
-
- $listenerId = 'security.authentication.listener.wsse.'.$id;
- $listener = $container->setDefinition($listenerId, new DefinitionDecorator('wsse.security.authentication.listener'));
-
- return array($providerId, $listenerId, $defaultEntryPoint);
- }
-
- public function getPosition()
- {
- return 'pre_auth';
- }
-
- public function getKey()
- {
- return 'wsse';
- }
-
- public function addConfiguration(NodeDefinition $node)
- {
- }
-}
namespace Wallabag\ApiBundle\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
-use Symfony\Component\Config\FileLocator;
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
-use Symfony\Component\DependencyInjection\Loader;
class WallabagApiExtension extends Extension
{
{
$configuration = new Configuration();
$config = $this->processConfiguration($configuration, $configs);
-
- $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
- $loader->load('services.yml');
}
public function getAlias()
--- /dev/null
+<?php
+
+namespace Wallabag\ApiBundle\Entity;
+
+use FOS\OAuthServerBundle\Entity\AccessToken as BaseAccessToken;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Table("oauth2_access_tokens")
+ * @ORM\Entity
+ */
+class AccessToken extends BaseAccessToken
+{
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="integer")
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ protected $id;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Client")
+ * @ORM\JoinColumn(nullable=false)
+ */
+ protected $client;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
+ */
+ protected $user;
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ApiBundle\Entity;
+
+use FOS\OAuthServerBundle\Entity\AuthCode as BaseAuthCode;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Table("oauth2_auth_codes")
+ * @ORM\Entity
+ */
+class AuthCode extends BaseAuthCode
+{
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="integer")
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ protected $id;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Client")
+ * @ORM\JoinColumn(nullable=false)
+ */
+ protected $client;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
+ */
+ protected $user;
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ApiBundle\Entity;
+
+use FOS\OAuthServerBundle\Entity\Client as BaseClient;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Table("oauth2_clients")
+ * @ORM\Entity
+ */
+class Client extends BaseClient
+{
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="integer")
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ protected $id;
+
+ public function __construct()
+ {
+ parent::__construct();
+ }
+}
--- /dev/null
+<?php
+
+namespace Wallabag\ApiBundle\Entity;
+
+use FOS\OAuthServerBundle\Entity\RefreshToken as BaseRefreshToken;
+use Doctrine\ORM\Mapping as ORM;
+
+/**
+ * @ORM\Table("oauth2_refresh_tokens")
+ * @ORM\Entity
+ */
+class RefreshToken extends BaseRefreshToken
+{
+ /**
+ * @ORM\Id
+ * @ORM\Column(type="integer")
+ * @ORM\GeneratedValue(strategy="AUTO")
+ */
+ protected $id;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Client")
+ * @ORM\JoinColumn(nullable=false)
+ */
+ protected $client;
+
+ /**
+ * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User")
+ */
+ protected $user;
+}
+++ /dev/null
-services:
- wsse.security.authentication.provider:
- class: Wallabag\ApiBundle\Security\Authentication\Provider\WsseProvider
- public: false
- arguments: ['', '%kernel.cache_dir%/security/nonces']
-
- wsse.security.authentication.listener:
- class: Wallabag\ApiBundle\Security\Firewall\WsseListener
- public: false
- tags:
- - { name: monolog.logger, channel: wsse }
- arguments: ['@security.context', '@security.authentication.manager', '@logger']
+++ /dev/null
-<?php
-
-namespace Wallabag\ApiBundle\Security\Authentication\Provider;
-
-use Symfony\Component\Security\Core\Authentication\Provider\AuthenticationProviderInterface;
-use Symfony\Component\Security\Core\User\UserProviderInterface;
-use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\Exception\NonceExpiredException;
-use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
-use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
-
-class WsseProvider implements AuthenticationProviderInterface
-{
- private $userProvider;
- private $cacheDir;
-
- public function __construct(UserProviderInterface $userProvider, $cacheDir)
- {
- $this->userProvider = $userProvider;
- $this->cacheDir = $cacheDir;
-
- // If cache directory does not exist we create it
- if (!is_dir($this->cacheDir)) {
- mkdir($this->cacheDir, 0777, true);
- }
- }
-
- public function authenticate(TokenInterface $token)
- {
- $user = $this->userProvider->loadUserByUsername($token->getUsername());
-
- if (!$user) {
- throw new AuthenticationException('Bad credentials. Did you forgot your username?');
- }
-
- if ($user && $this->validateDigest($token->digest, $token->nonce, $token->created, $user->getPassword())) {
- $authenticatedToken = new WsseUserToken($user->getRoles());
- $authenticatedToken->setUser($user);
-
- return $authenticatedToken;
- }
-
- throw new AuthenticationException('The WSSE authentication failed.');
- }
-
- protected function validateDigest($digest, $nonce, $created, $secret)
- {
- // Check created time is not in the future
- if (strtotime($created) > time()) {
- throw new AuthenticationException('Back to the future...');
- }
-
- // Expire timestamp after 5 minutes
- if (time() - strtotime($created) > 300) {
- throw new AuthenticationException('Too late for this timestamp... Watch your watch.');
- }
-
- // Validate nonce is unique within 5 minutes
- if (file_exists($this->cacheDir.'/'.$nonce) && file_get_contents($this->cacheDir.'/'.$nonce) + 300 > time()) {
- throw new NonceExpiredException('Previously used nonce detected');
- }
-
- file_put_contents($this->cacheDir.'/'.$nonce, time());
-
- // Validate Secret
- $expected = base64_encode(sha1(base64_decode($nonce).$created.$secret, true));
-
- if ($digest !== $expected) {
- throw new AuthenticationException('Bad credentials ! Digest is not as expected.');
- }
-
- return $digest === $expected;
- }
-
- public function supports(TokenInterface $token)
- {
- return $token instanceof WsseUserToken;
- }
-}
+++ /dev/null
-<?php
-
-namespace Wallabag\ApiBundle\Security\Authentication\Token;
-
-use Symfony\Component\Security\Core\Authentication\Token\AbstractToken;
-
-class WsseUserToken extends AbstractToken
-{
- public $created;
- public $digest;
- public $nonce;
-
- public function __construct(array $roles = array())
- {
- parent::__construct($roles);
-
- $this->setAuthenticated(count($roles) > 0);
- }
-
- public function getCredentials()
- {
- return '';
- }
-}
+++ /dev/null
-<?php
-
-namespace Wallabag\ApiBundle\Security\Firewall;
-
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\Security\Http\Firewall\ListenerInterface;
-use Symfony\Component\Security\Core\Exception\AuthenticationException;
-use Symfony\Component\Security\Core\SecurityContextInterface;
-use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
-use Wallabag\ApiBundle\Security\Authentication\Token\WsseUserToken;
-use Psr\Log\LoggerInterface;
-
-class WsseListener implements ListenerInterface
-{
- protected $securityContext;
- protected $authenticationManager;
- protected $logger;
-
- public function __construct(SecurityContextInterface $securityContext, AuthenticationManagerInterface $authenticationManager, LoggerInterface $logger)
- {
- $this->securityContext = $securityContext;
- $this->authenticationManager = $authenticationManager;
- $this->logger = $logger;
- }
-
- public function handle(GetResponseEvent $event)
- {
- $request = $event->getRequest();
-
- $wsseRegex = '/UsernameToken Username="([^"]+)", PasswordDigest="([^"]+)", Nonce="([^"]+)", Created="([^"]+)"/';
- if (!$request->headers->has('x-wsse') || 1 !== preg_match($wsseRegex, $request->headers->get('x-wsse'), $matches)) {
- return;
- }
-
- $token = new WsseUserToken();
- $token->setUser($matches[1]);
-
- $token->digest = $matches[2];
- $token->nonce = $matches[3];
- $token->created = $matches[4];
-
- try {
- $authToken = $this->authenticationManager->authenticate($token);
-
- $this->securityContext->setToken($authToken);
-
- return;
- } catch (AuthenticationException $failed) {
- $failedMessage = 'WSSE Login failed for '.$token->getUsername().'. Why ? '.$failed->getMessage();
- $this->logger->err($failedMessage);
-
- // Deny authentication with a '403 Forbidden' HTTP response
- $response = new Response();
- $response->setStatusCode(403);
- $response->setContent($failedMessage);
- $event->setResponse($response);
-
- return;
- }
- }
-}
--- /dev/null
+<?php
+
+namespace Wallabag\ApiBundle\Tests;
+
+use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+use Symfony\Component\BrowserKit\Cookie;
+
+abstract class AbstractControllerTest extends WebTestCase
+{
+ /**
+ * @var Client
+ */
+ protected $client = null;
+
+ public function setUp()
+ {
+ $this->client = $this->createAuthorizedClient();
+ }
+
+ /**
+ * @return Client
+ */
+ protected function createAuthorizedClient()
+ {
+ $client = static::createClient();
+ $container = $client->getContainer();
+
+ $session = $container->get('session');
+ /** @var $userManager \FOS\UserBundle\Doctrine\UserManager */
+ $userManager = $container->get('fos_user.user_manager');
+ /** @var $loginManager \FOS\UserBundle\Security\LoginManager */
+ $loginManager = $container->get('fos_user.security.login_manager');
+ $firewallName = $container->getParameter('fos_user.firewall_name');
+
+ $user = $userManager->findUserBy(array('username' => 'admin'));
+ $loginManager->loginUser($firewallName, $user);
+
+ // save the login token into the session and put it in a cookie
+ $container->get('session')->set('_security_'.$firewallName,
+ serialize($container->get('security.context')->getToken()));
+ $container->get('session')->save();
+ $client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
+
+ return $client;
+ }
+}
namespace Wallabag\ApiBundle\Tests\Controller;
-use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
+use Wallabag\ApiBundle\Tests\AbstractControllerTest;
-class WallabagRestControllerTest extends WebTestCase
+class WallabagRestControllerTest extends AbstractControllerTest
{
protected static $salt;
- /**
- * Grab the salt once and store it to be available for all tests.
- */
- public static function setUpBeforeClass()
- {
- $client = self::createClient();
-
- $user = $client->getContainer()
- ->get('doctrine.orm.entity_manager')
- ->getRepository('WallabagCoreBundle:User')
- ->findOneByUsername('admin');
-
- self::$salt = $user->getSalt();
- }
-
- /**
- * Generate HTTP headers for authenticate user on API.
- *
- * @param string $username
- * @param string $password
- *
- * @return array
- */
- private function generateHeaders($username, $password)
- {
- $encryptedPassword = sha1($password.$username.self::$salt);
- $nonce = substr(md5(uniqid('nonce_', true)), 0, 16);
-
- $now = new \DateTime('now', new \DateTimeZone('UTC'));
- $created = (string) $now->format('Y-m-d\TH:i:s\Z');
- $digest = base64_encode(sha1(base64_decode($nonce).$created.$encryptedPassword, true));
-
- return array(
- 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
- 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="'.$username.'", PasswordDigest="'.$digest.'", Nonce="'.$nonce.'", Created="'.$created.'"',
- );
- }
-
- public function testGetSalt()
- {
- $client = $this->createClient();
- $client->request('GET', '/api/salts/admin.json');
-
- $user = $client->getContainer()
- ->get('doctrine.orm.entity_manager')
- ->getRepository('WallabagCoreBundle:User')
- ->findOneByUsername('admin');
-
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
-
- $content = json_decode($client->getResponse()->getContent(), true);
-
- $this->assertArrayHasKey(0, $content);
- $this->assertEquals($user->getSalt(), $content[0]);
-
- $client->request('GET', '/api/salts/notfound.json');
- $this->assertEquals(404, $client->getResponse()->getStatusCode());
- }
-
- public function testWithBadHeaders()
- {
- $client = $this->createClient();
-
- $entry = $client->getContainer()
- ->get('doctrine.orm.entity_manager')
- ->getRepository('WallabagCoreBundle:Entry')
- ->findOneByIsArchived(false);
-
- if (!$entry) {
- $this->markTestSkipped('No content found in db.');
- }
-
- $badHeaders = array(
- 'HTTP_AUTHORIZATION' => 'Authorization profile="UsernameToken"',
- 'HTTP_x-wsse' => 'X-WSSE: UsernameToken Username="admin", PasswordDigest="Wr0ngDig3st", Nonce="n0Nc3", Created="2015-01-01T13:37:00Z"',
- );
-
- $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $badHeaders);
- $this->assertEquals(403, $client->getResponse()->getStatusCode());
- }
-
public function testGetOneEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 1, 'isArchived' => false));
$this->markTestSkipped('No content found in db.');
}
- $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
+ $this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
-
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
$this->assertCount(count($entry->getTags()), $content['tags']);
$this->assertTrue(
- $client->getResponse()->headers->contains(
+ $this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
public function testGetOneEntryWrongUser()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneBy(array('user' => 2, 'isArchived' => false));
$this->markTestSkipped('No content found in db.');
}
- $client->request('GET', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
+ $this->client->request('GET', '/api/entries/'.$entry->getId().'.json');
- $this->assertEquals(403, $client->getResponse()->getStatusCode());
+ $this->assertEquals(403, $this->client->getResponse()->getStatusCode());
}
public function testGetEntries()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('GET', '/api/entries', array(), array(), $headers);
+ $this->client->request('GET', '/api/entries');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
- $client->getResponse()->headers->contains(
+ $this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
public function testGetStarredEntries()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
+ $this->client->request('GET', '/api/entries', array('star' => 1, 'sort' => 'updated'));
- $client->request('GET', '/api/entries', array('star' => 1, 'sort' => 'updated'), array(), $headers);
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
-
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
- $client->getResponse()->headers->contains(
+ $this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
public function testGetArchiveEntries()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('GET', '/api/entries', array('archive' => 1), array(), $headers);
+ $this->client->request('GET', '/api/entries', array('archive' => 1));
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThanOrEqual(1, count($content));
$this->assertNotEmpty($content['_embedded']['items']);
$this->assertGreaterThanOrEqual(1, $content['pages']);
$this->assertTrue(
- $client->getResponse()->headers->contains(
+ $this->client->getResponse()->headers->contains(
'Content-Type',
'application/json'
)
public function testDeleteEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
$this->markTestSkipped('No content found in db.');
}
- $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
+ $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getTitle(), $content['title']);
$this->assertEquals($entry->getUrl(), $content['url']);
// We'll try to delete this entry again
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('DELETE', '/api/entries/'.$entry->getId().'.json', array(), array(), $headers);
+ $this->client->request('DELETE', '/api/entries/'.$entry->getId().'.json');
- $this->assertEquals(404, $client->getResponse()->getStatusCode());
+ $this->assertEquals(404, $this->client->getResponse()->getStatusCode());
}
public function testPostEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('POST', '/api/entries.json', array(
+ $this->client->request('POST', '/api/entries.json', array(
'url' => 'http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html',
'tags' => 'google',
- ), array(), $headers);
+ ));
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content['id']);
$this->assertEquals('http://www.lemonde.fr/pixels/article/2015/03/28/plongee-dans-l-univers-d-ingress-le-jeu-de-google-aux-frontieres-du-reel_4601155_4408996.html', $content['url']);
public function testPatchEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
// hydrate the tags relations
$nbTags = count($entry->getTags());
- $client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array(
+ $this->client->request('PATCH', '/api/entries/'.$entry->getId().'.json', array(
'title' => 'New awesome title',
'tags' => 'new tag '.uniqid(),
'star' => true,
'archive' => false,
- ), array(), $headers);
+ ));
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertEquals($entry->getId(), $content['id']);
$this->assertEquals($entry->getUrl(), $content['url']);
public function testGetTagsEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneWithTags(1);
$tags[] = array('id' => $tag->getId(), 'label' => $tag->getLabel());
}
- $client->request('GET', '/api/entries/'.$entry->getId().'/tags', array(), array(), $headers);
+ $this->client->request('GET', '/api/entries/'.$entry->getId().'/tags');
- $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $client->getResponse()->getContent());
+ $this->assertEquals(json_encode($tags, JSON_HEX_QUOT), $this->client->getResponse()->getContent());
}
public function testPostTagsOnEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->findOneByUser(1);
$newTags = 'tag1,tag2,tag3';
- $client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags), array(), $headers);
+ $this->client->request('POST', '/api/entries/'.$entry->getId().'/tags', array('tags' => $newTags));
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags + 3, count($content['tags']));
- $entryDB = $client->getContainer()
+ $entryDB = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
->find($entry->getId());
}
}
- public function testDeleteOneTagEntrie()
+ public function testDeleteOneTagEntry()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $entry = $client->getContainer()
+ $entry = $this->client->getContainer()
->get('doctrine.orm.entity_manager')
->getRepository('WallabagCoreBundle:Entry')
- ->findOneByUser(1);
+ ->findOneWithTags(1);
+ $entry = $entry[0];
if (!$entry) {
$this->markTestSkipped('No content found in db.');
$nbTags = count($entry->getTags());
$tag = $entry->getTags()[0];
- $client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json', array(), array(), $headers);
+ $this->client->request('DELETE', '/api/entries/'.$entry->getId().'/tags/'.$tag->getId().'.json');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('tags', $content);
$this->assertEquals($nbTags - 1, count($content['tags']));
public function testGetUserTags()
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('GET', '/api/tags.json', array(), array(), $headers);
+ $this->client->request('GET', '/api/tags.json');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertGreaterThan(0, $content);
$this->assertArrayHasKey('id', $content[0]);
*/
public function testDeleteUserTag($tag)
{
- $client = $this->createClient();
- $headers = $this->generateHeaders('admin', 'mypassword');
-
- $client->request('DELETE', '/api/tags/'.$tag['id'].'.json', array(), array(), $headers);
+ $this->client->request('DELETE', '/api/tags/'.$tag['id'].'.json');
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
+ $this->assertEquals(200, $this->client->getResponse()->getStatusCode());
- $content = json_decode($client->getResponse()->getContent(), true);
+ $content = json_decode($this->client->getResponse()->getContent(), true);
$this->assertArrayHasKey('label', $content);
$this->assertEquals($tag['label'], $content['label']);
namespace Wallabag\ApiBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
-use Wallabag\ApiBundle\DependencyInjection\Security\Factory\WsseFactory;
-use Symfony\Component\DependencyInjection\ContainerBuilder;
class WallabagApiBundle extends Bundle
{
- public function build(ContainerBuilder $container)
- {
- parent::build($container);
-
- $extension = $container->getExtension('security');
- $extension->addSecurityListenerFactory(new WsseFactory());
- }
}
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Output\NullOutput;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Config;
class InstallCommand extends ContainerAwareCommand
$em = $this->getContainer()->get('doctrine.orm.entity_manager');
- $user = new User();
+ $userManager = $this->getContainer()->get('fos_user.user_manager');
+ $user = $userManager->createUser();
$user->setUsername($dialog->ask($this->defaultOutput, '<question>Username</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
- $user->setPassword($dialog->ask($this->defaultOutput, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
+ $user->setPlainPassword($dialog->ask($this->defaultOutput, '<question>Password</question> <comment>(default: wallabag)</comment> :', 'wallabag'));
$user->setEmail($dialog->ask($this->defaultOutput, '<question>Email:</question>', ''));
$user->setEnabled(true);
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\JsonResponse;
use Wallabag\CoreBundle\Entity\Config;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Form\Type\ChangePasswordType;
use Wallabag\CoreBundle\Form\Type\UserInformationType;
use Wallabag\CoreBundle\Form\Type\NewUserType;
{
$em = $this->getDoctrine()->getManager();
$config = $this->getConfig();
+ $userManager = $this->container->get('fos_user.user_manager');
$user = $this->getUser();
// handle basic config detail (this form is defined as a service)
$pwdForm->handleRequest($request);
if ($pwdForm->isValid()) {
- $user->setPassword($pwdForm->get('new_password')->getData());
- $em->persist($user);
- $em->flush();
+ $user->setPlainPassword($pwdForm->get('new_password')->getData());
+ $userManager->updateUser($user, true);
$this->get('session')->getFlashBag()->add(
'notice',
$userForm->handleRequest($request);
if ($userForm->isValid()) {
- $em->persist($user);
- $em->flush();
+ $userManager->updateUser($user, true);
$this->get('session')->getFlashBag()->add(
'notice',
}
// handle adding new user
- $newUser = new User();
+ $newUser = $userManager->createUser();
// enable created user by default
$newUser->setEnabled(true);
$newUserForm = $this->createForm(new NewUserType(), $newUser, array('validation_groups' => array('Profile')));
$newUserForm->handleRequest($request);
- if ($newUserForm->isValid()) {
- $em->persist($newUser);
+ if ($newUserForm->isValid() && $this->get('security.authorization_checker')->isGranted('ROLE_SUPER_ADMIN')) {
+ $userManager->updateUser($newUser, true);
$config = new Config($newUser);
$config->setTheme($this->container->getParameter('theme'));
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Entity\Entry;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta;
* Shows unread entries for current user.
*
* @Route("/{username}/{token}/unread.xml", name="unread_rss", defaults={"_format"="xml"})
- * @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
+ * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
* Shows read entries for current user.
*
* @Route("/{username}/{token}/archive.xml", name="archive_rss")
- * @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
+ * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
* Shows starred entries for current user.
*
* @Route("/{username}/{token}/starred.xml", name="starred_rss")
- * @ParamConverter("user", class="WallabagCoreBundle:User", converter="username_rsstoken_converter")
+ * @ParamConverter("user", class="WallabagUserBundle:User", converter="username_rsstoken_converter")
*
* @return \Symfony\Component\HttpFoundation\Response
*/
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Controller;
-
-use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
-use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
-use Symfony\Bundle\FrameworkBundle\Controller\Controller;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\Security\Core\SecurityContext;
-use Wallabag\CoreBundle\Form\Type\ResetPasswordType;
-
-class SecurityController extends Controller
-{
- public function loginAction(Request $request)
- {
- $session = $request->getSession();
- // get the login error if there is one
- if ($request->attributes->has(SecurityContext::AUTHENTICATION_ERROR)) {
- $error = $request->attributes->get(SecurityContext::AUTHENTICATION_ERROR);
- } else {
- $error = $session->get(SecurityContext::AUTHENTICATION_ERROR);
- $session->remove(SecurityContext::AUTHENTICATION_ERROR);
- }
-
- return $this->render('WallabagCoreBundle:Security:login.html.twig', array(
- // last username entered by the user
- 'last_username' => $session->get(SecurityContext::LAST_USERNAME),
- 'error' => $error,
- ));
- }
-
- /**
- * Request forgot password: show form.
- *
- * @Route("/forgot-password", name="forgot_password")
- *
- * @Method({"GET", "POST"})
- */
- public function forgotPasswordAction(Request $request)
- {
- $form = $this->createForm('forgot_password');
- $form->handleRequest($request);
-
- if ($form->isValid()) {
- $user = $this->getDoctrine()->getRepository('WallabagCoreBundle:User')->findOneByEmail($form->get('email')->getData());
-
- // generate "hard" token
- $user->setConfirmationToken(rtrim(strtr(base64_encode(hash('sha256', uniqid(mt_rand(), true), true)), '+/', '-_'), '='));
- $user->setPasswordRequestedAt(new \DateTime());
-
- $em = $this->getDoctrine()->getManager();
- $em->persist($user);
- $em->flush();
-
- $message = \Swift_Message::newInstance()
- ->setSubject('Reset Password')
- ->setFrom($this->container->getParameter('from_email'))
- ->setTo($user->getEmail())
- ->setBody($this->renderView('WallabagCoreBundle:Mail:forgotPassword.txt.twig', array(
- 'username' => $user->getUsername(),
- 'confirmationUrl' => $this->generateUrl('forgot_password_reset', array('token' => $user->getConfirmationToken()), true),
- )))
- ;
- $this->get('mailer')->send($message);
-
- return $this->redirect($this->generateUrl('forgot_password_check_email',
- array('email' => $this->getObfuscatedEmail($user->getEmail()))
- ));
- }
-
- return $this->render('WallabagCoreBundle:Security:forgotPassword.html.twig', array(
- 'form' => $form->createView(),
- ));
- }
-
- /**
- * Tell the user to check his email provider.
- *
- * @Route("/forgot-password/check-email", name="forgot_password_check_email")
- *
- * @Method({"GET"})
- */
- public function checkEmailAction(Request $request)
- {
- $email = $request->query->get('email');
-
- if (empty($email)) {
- // the user does not come from the forgotPassword action
- return $this->redirect($this->generateUrl('forgot_password'));
- }
-
- return $this->render('WallabagCoreBundle:Security:checkEmail.html.twig', array(
- 'email' => $email,
- ));
- }
-
- /**
- * Reset user password.
- *
- * @Route("/forgot-password/{token}", name="forgot_password_reset")
- *
- * @Method({"GET", "POST"})
- */
- public function resetAction(Request $request, $token)
- {
- $user = $this->getDoctrine()->getRepository('WallabagCoreBundle:User')->findOneByConfirmationToken($token);
-
- if (null === $user) {
- throw $this->createNotFoundException(sprintf('No user found with token "%s"', $token));
- }
-
- $form = $this->createForm(new ResetPasswordType());
- $form->handleRequest($request);
-
- if ($form->isValid()) {
- $user->setPassword($form->get('new_password')->getData());
-
- $em = $this->getDoctrine()->getManager();
- $em->persist($user);
- $em->flush();
-
- $this->get('session')->getFlashBag()->add(
- 'notice',
- 'The password has been reset successfully'
- );
-
- return $this->redirect($this->generateUrl('login'));
- }
-
- return $this->render('WallabagCoreBundle:Security:reset.html.twig', array(
- 'token' => $token,
- 'form' => $form->createView(),
- ));
- }
-
- /**
- * Get the truncated email displayed when requesting the resetting.
- *
- * Keeping only the part following @ in the address.
- *
- * @param string $email
- *
- * @return string
- */
- protected function getObfuscatedEmail($email)
- {
- if (false !== $pos = strpos($email, '@')) {
- $email = '...'.substr($email, $pos);
- }
-
- return $email;
- }
-}
private $rssLimit;
/**
- * @ORM\OneToOne(targetEntity="User", inversedBy="config")
+ * @ORM\OneToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="config")
*/
private $user;
/*
* @param User $user
*/
- public function __construct(User $user)
+ public function __construct(\Wallabag\UserBundle\Entity\User $user)
{
$this->user = $user;
}
/**
* Set user.
*
- * @param \Wallabag\CoreBundle\Entity\User $user
+ * @param User $user
*
* @return Config
*/
- public function setUser(\Wallabag\CoreBundle\Entity\User $user = null)
+ public function setUser(User $user = null)
{
$this->user = $user;
/**
* Get user.
*
- * @return \Wallabag\CoreBundle\Entity\User
+ * @return User
*/
public function getUser()
{
use Symfony\Component\Validator\Constraints as Assert;
use Hateoas\Configuration\Annotation as Hateoas;
use JMS\Serializer\Annotation\XmlRoot;
+use Wallabag\UserBundle\Entity\User;
/**
* Entry.
private $isPublic;
/**
- * @ORM\ManyToOne(targetEntity="User", inversedBy="entries")
+ * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="entries")
*/
private $user;
/*
* @param User $user
*/
- public function __construct(User $user)
+ public function __construct(\Wallabag\UserBundle\Entity\User $user)
{
$this->user = $user;
$this->tags = new ArrayCollection();
private $entries;
/**
- * @ORM\ManyToOne(targetEntity="User", inversedBy="tags")
+ * @ORM\ManyToOne(targetEntity="Wallabag\UserBundle\Entity\User", inversedBy="tags")
*/
private $user;
- public function __construct(User $user)
+ public function __construct(\Wallabag\UserBundle\Entity\User $user)
{
$this->user = $user;
$this->entries = new ArrayCollection();
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\EventListener;
+
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Doctrine\ORM\EntityManager;
+use FOS\UserBundle\Event\FilterUserResponseEvent;
+use FOS\UserBundle\FOSUserEvents;
+use Wallabag\CoreBundle\Entity\Config;
+
+class RegistrationConfirmedListener implements EventSubscriberInterface
+{
+ private $em;
+ private $theme;
+ private $itemsOnPage;
+ private $rssLimit;
+ private $language;
+
+ public function __construct(EntityManager $em, $theme, $itemsOnPage, $rssLimit, $language)
+ {
+ $this->em = $em;
+ $this->theme = $theme;
+ $this->itemsOnPage = $itemsOnPage;
+ $this->rssLimit = $rssLimit;
+ $this->language = $language;
+ }
+
+ public static function getSubscribedEvents()
+ {
+ return array(
+ FOSUserEvents::REGISTRATION_CONFIRMED => 'authenticate',
+ );
+ }
+
+ public function authenticate(FilterUserResponseEvent $event, $eventName = null, EventDispatcherInterface $eventDispatcher = null)
+ {
+ if (!$event->getUser()->isEnabled()) {
+ return;
+ }
+
+ $config = new Config($event->getUser());
+ $config->setTheme($this->theme);
+ $config->setItemsPerPage($this->itemsOnPage);
+ $config->setRssLimit($this->rssLimit);
+ $config->setLanguage($this->language);
+ $this->em->persist($config);
+ $this->em->flush();
+ }
+}
use Symfony\Component\OptionsResolver\OptionsResolver;
use Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface;
use Doctrine\ORM\EntityRepository;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
class EntryFilterType extends AbstractType
{
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Form\Type;
-
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Validator\Constraints;
-use Symfony\Component\Validator\ExecutionContextInterface;
-use Doctrine\Bundle\DoctrineBundle\Registry;
-
-class ForgotPasswordType extends AbstractType
-{
- private $doctrine = null;
-
- public function __construct(Registry $doctrine)
- {
- $this->doctrine = $doctrine;
- }
-
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder
- ->add('email', 'email', array(
- 'required' => true,
- 'constraints' => array(
- new Constraints\Email(),
- new Constraints\NotBlank(),
- new Constraints\Callback(array(array($this, 'validateEmail'))),
- ),
- ))
- ;
- }
-
- public function getName()
- {
- return 'forgot_password';
- }
-
- public function validateEmail($email, ExecutionContextInterface $context)
- {
- $user = $this->doctrine
- ->getRepository('WallabagCoreBundle:User')
- ->findOneByEmail($email);
-
- if (!$user) {
- $context->addViolationAt(
- 'email',
- 'No user found with this email',
- array(),
- $email
- );
- }
- }
-}
{
$builder
->add('username', 'text', array('required' => true))
- ->add('password', 'password', array(
+ ->add('plainPassword', 'repeated', array(
+ 'type' => 'password',
'constraints' => array(
new Constraints\Length(array(
'min' => 8,
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
- 'data_class' => 'Wallabag\CoreBundle\Entity\User',
+ 'data_class' => 'Wallabag\UserBundle\Entity\User',
));
}
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Form\Type;
-
-use Symfony\Component\Form\AbstractType;
-use Symfony\Component\Form\FormBuilderInterface;
-use Symfony\Component\Validator\Constraints;
-
-class ResetPasswordType extends AbstractType
-{
- public function buildForm(FormBuilderInterface $builder, array $options)
- {
- $builder
- ->add('new_password', 'repeated', array(
- 'type' => 'password',
- 'invalid_message' => 'The password fields must match.',
- 'required' => true,
- 'first_options' => array('label' => 'New password'),
- 'second_options' => array('label' => 'Repeat new password'),
- 'constraints' => array(
- new Constraints\Length(array(
- 'min' => 8,
- 'minMessage' => 'Password should by at least 8 chars long',
- )),
- new Constraints\NotBlank(),
- ),
- ))
- ;
- }
-
- public function getName()
- {
- return 'change_passwd';
- }
-}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
- 'data_class' => 'Wallabag\CoreBundle\Entity\User',
+ 'data_class' => 'Wallabag\UserBundle\Entity\User',
));
}
use Liip\ThemeBundle\Helper\DeviceDetectionInterface;
use Symfony\Component\Security\Core\SecurityContextInterface;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
/**
* This class intend to detect the active theme for the logged in user.
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
/**
* ParamConverter used in the RSS controller to retrieve the right user according to
$em = $this->registry->getManagerForClass($configuration->getClass());
// Check, if class name is what we need
- if ('Wallabag\CoreBundle\Entity\User' !== $em->getClassMetadata($configuration->getClass())->getName()) {
+ if ('Wallabag\UserBundle\Entity\User' !== $em->getClassMetadata($configuration->getClass())->getName()) {
return false;
}
tags:
- { name: form.type, alias: config }
+ wallabag_core.form.registration:
+ class: Wallabag\CoreBundle\Form\Type\RegistrationType
+ tags:
+ - { name: form.type, alias: wallabag_user_registration }
+
wallabag_core.form.type.forgot_password:
class: Wallabag\CoreBundle\Form\Type\ForgotPasswordType
arguments:
wallabag_core.doctrine.prefixed_naming_strategy:
class: Wallabag\CoreBundle\Doctrine\Mapping\PrefixedNamingStrategy
- arguments: [%database_table_prefix%]
+ arguments:
+ - %database_table_prefix%
wallabag_core.graby:
class: Graby\Graby
class: Wallabag\CoreBundle\Helper\ContentProxy
arguments:
- @wallabag_core.graby
+
+ wallabag_core.registration_confirmed:
+ class: Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener
+ arguments:
+ - @doctrine.orm.entity_manager
+ - %theme%
+ - %items_on_page%
+ - %rss_limit%
+ - %language%
+ tags:
+ - { name: kernel.event_subscriber }
<script src="{{ asset('themes/_global/js/bookmarklet.js') }}"></script>
{% endblock %}
- <title>{% block title %}{% endblock %} - wallabag</title>
+ <title>{% block title %}{% endblock %}</title>
{% endblock %}
</head>
{% block messages %}{% endblock %}
- <div id="content" class="w600p">
+ <div id="content">
{% block content %}{% endblock %}
</div>
</main>
{{ form_rest(form.pwd) }}
</form>
+ {% if is_granted('ROLE_SUPER_ADMIN') %}
<h2>{% trans %}Add a user{% endtrans %}</h2>
<form action="{{ path('config') }}" method="post" {{ form_enctype(form.new_user) }}>
<fieldset class="w500p inline">
<div class="row">
- {{ form_label(form.new_user.password) }}
- {{ form_errors(form.new_user.password) }}
- {{ form_widget(form.new_user.password) }}
+ {{ form_label(form.new_user.plainPassword.first) }}
+ {{ form_errors(form.new_user.plainPassword.first) }}
+ {{ form_widget(form.new_user.plainPassword.first) }}
+ </div>
+ </fieldset>
+
+ <fieldset class="w500p inline">
+ <div class="row">
+ {{ form_label(form.new_user.plainPassword.second) }}
+ {{ form_errors(form.new_user.plainPassword.second) }}
+ {{ form_widget(form.new_user.plainPassword.second) }}
</div>
</fieldset>
</fieldset>
{{ form_rest(form.new_user) }}
+ {% endif %}
</form>
{% endblock %}
+++ /dev/null
-{% extends "WallabagCoreBundle::layout.html.twig" %}
-
-{% block title %}{% trans %}Forgot password{% endtrans %}{% endblock %}
-
-{% block body_class %}login{% endblock %}
-
-{% block menu %}{% endblock %}
-
-{% block content %}
- <form action="{{ path('forgot_password') }}" method="post" name="forgotPasswordform">
- <fieldset class="w500p center">
- <h2 class="mbs txtcenter">{% trans %}Forgot password{% endtrans %}</h2>
-
- {{ form_errors(form) }}
-
- <p>Enter your email address below and we'll send you password reset instructions.</p>
-
- <div class="row">
- {{ form_label(form.email) }}
- {{ form_errors(form.email) }}
- {{ form_widget(form.email) }}
- </div>
-
- <div class="row mts txtcenter">
- <button type="submit">Send me reset instructions</button>
- </div>
- </fieldset>
-
- {{ form_rest(form) }}
- </form>
-{% endblock %}
+++ /dev/null
-{% extends "WallabagCoreBundle::layout.html.twig" %}
-
-{% block title %}{% trans %}Change password{% endtrans %}{% endblock %}
-
-{% block body_class %}login{% endblock %}
-
-{% block menu %}{% endblock %}
-
-{% block content %}
- <form action="{{ path('forgot_password_reset', {'token': token}) }}" method="post" name="loginform">
- <fieldset class="w500p center">
- <h2 class="mbs txtcenter">{% trans %}Change password{% endtrans %}</h2>
-
- {{ form_errors(form) }}
-
- <div class="row">
- {{ form_label(form.new_password.first) }}
- {{ form_errors(form.new_password.first) }}
- {{ form_widget(form.new_password.first) }}
- </div>
-
- <div class="row">
- {{ form_label(form.new_password.second) }}
- {{ form_errors(form.new_password.second) }}
- {{ form_widget(form.new_password.second) }}
- </div>
-
- <div class="row mts txtcenter">
- <button type="submit">Change password</button>
- </div>
- </fieldset>
-
- {{ form_rest(form) }}
- </form>
-{% endblock %}
</li>
<li><a href="{{ path('config') }}">{% trans %}config{% endtrans %}</a></li>
<li><a href="{{ path('about') }}">{% trans %}about{% endtrans %}</a></li>
- <li><a class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
+ <li><a class="icon icon-power" href="{{ path('fos_user_security_logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
</ul>
{% endblock %}
<li class="tab col s3"><a href="#set2">{% trans %}RSS{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set3">{% trans %}User information{% endtrans %}</a></li>
<li class="tab col s3"><a href="#set4">{% trans %}Password{% endtrans %}</a></li>
+ {% if is_granted('ROLE_SUPER_ADMIN') %}
<li class="tab col s3"><a href="#set5">{% trans %}Add a user{% endtrans %}</a></li>
+ {% endif %}
</ul>
</div>
</form>
</div>
-
+ {% if is_granted('ROLE_SUPER_ADMIN') %}
<div id="set5" class="col s12">
<form action="{{ path('config') }}#set5" method="post" {{ form_enctype(form.new_user) }}>
{{ form_errors(form.new_user) }}
<div class="row">
<div class="input-field col s12">
- {{ form_label(form.new_user.password) }}
- {{ form_errors(form.new_user.password) }}
- {{ form_widget(form.new_user.password) }}
+ {{ form_label(form.new_user.plainPassword.first) }}
+ {{ form_errors(form.new_user.plainPassword.first) }}
+ {{ form_widget(form.new_user.plainPassword.first) }}
+ </div>
+ </div>
+
+ <div class="row">
+ <div class="input-field col s12">
+ {{ form_label(form.new_user.plainPassword.second) }}
+ {{ form_errors(form.new_user.plainPassword.second) }}
+ {{ form_widget(form.new_user.plainPassword.second) }}
</div>
</div>
</form>
</div>
+ {% endif %}
</div>
</div>
{% trans %}Login{% endtrans %}
<i class="mdi-content-send right"></i>
</button>
+ <a href="{{ path('fos_user_registration_register') }}">{% trans %}Register{% endtrans %}</a>
</div>
</form>
</div>
<div class="center">
- <a href="{{ path('forgot_password') }}">{% trans %}Forgot your password?{% endtrans %}</a>
+ <a href="{{ path('fos_user_resetting_request') }}">{% trans %}Forgot your password?{% endtrans %}</a>
</div>
</div>
</main>
<li class="bold border-bottom {% if currentRoute == 'tags' %}active{% endif %}"><a class="waves-effect" href="{{ path('tag') }}">{% trans %}tags{% endtrans %}</a></li>
<li class="bold {% if currentRoute == 'config' %}active{% endif %}"><a class="waves-effect" href="{{ path('config') }}">{% trans %}config{% endtrans %}</a></li>
<li class="bold {% if currentRoute == 'howto' %}active{% endif %}"><a class="waves-effect" href="{{ path('howto') }}">{% trans %}howto{% endtrans %}</a></li>
- <li class="bold border-bottom {% if currentRoute == 'about' %}active{% endif %}"><a class="waves-effect" href="{{ path('about') }}">{% trans %}About{% endtrans %}</a></li>
- <li class="bold"><a class="waves-effect" class="icon icon-power" href="{{ path('logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
+ <li class="bold"><a class="waves-effect" class="icon icon-power" href="{{ path('fos_user_security_logout') }}" title="{% trans %}logout{% endtrans %}">{% trans %}logout{% endtrans %}</a></li>
</ul>
<div class="nav-wrapper nav-panels">
<a href="#" data-activates="slide-out" class="nav-panel-menu button-collapse"><i class="mdi-navigation-menu"></i></a>
background: #f0f0f0;
}
+body.login main {
+ padding: 0;
+}
+
#warning_message {
position: fixed;
background-color: #ff6347;
}
.card .card-action a {
+ color: #ffffff;
margin: 0;
}
+.card .card-action a:hover {
+ color: #ffffff;
+}
+
.settings .div_tabs {
padding-bottom: 15px;
}
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Security\Authentication\Encoder;
-
-use Symfony\Component\Security\Core\Encoder\BasePasswordEncoder;
-use Symfony\Component\Security\Core\Exception\BadCredentialsException;
-
-/**
- * This override just add en extra variable (username) to be able to salt the password
- * the way Wallabag v1 does. It will avoid to break compatibility with Wallabag v1.
- */
-class WallabagPasswordEncoder extends BasePasswordEncoder
-{
- private $algorithm;
- private $encodeHashAsBase64;
- private $iterations;
- private $username = null;
-
- /**
- * Constructor.
- *
- * @param string $algorithm The digest algorithm to use
- * @param bool $encodeHashAsBase64 Whether to base64 encode the password hash
- * @param int $iterations The number of iterations to use to stretch the password hash
- */
- public function __construct($algorithm = 'sha512', $encodeHashAsBase64 = true, $iterations = 5000)
- {
- $this->algorithm = $algorithm;
- $this->encodeHashAsBase64 = $encodeHashAsBase64;
- $this->iterations = $iterations;
- }
-
- public function setUsername($username)
- {
- $this->username = $username;
- }
-
- /**
- * {@inheritdoc}
- */
- public function encodePassword($raw, $salt)
- {
- if ($this->isPasswordTooLong($raw)) {
- throw new BadCredentialsException('Invalid password.');
- }
-
- if (!in_array($this->algorithm, hash_algos(), true)) {
- throw new \LogicException(sprintf('The algorithm "%s" is not supported.', $this->algorithm));
- }
-
- $salted = $this->mergePasswordAndSalt($raw, $salt);
- $digest = hash($this->algorithm, $salted, true);
-
- // "stretch" hash
- for ($i = 1; $i < $this->iterations; ++$i) {
- $digest = hash($this->algorithm, $digest.$salted, true);
- }
-
- return $this->encodeHashAsBase64 ? base64_encode($digest) : bin2hex($digest);
- }
-
- /**
- * {@inheritdoc}
- *
- * We inject the username inside the salted password
- */
- protected function mergePasswordAndSalt($password, $salt)
- {
- if (null === $this->username) {
- throw new \LogicException('We can not check the password without a username.');
- }
-
- if (empty($salt)) {
- return $password;
- }
-
- return $password.$this->username.$salt;
- }
-
- /**
- * {@inheritdoc}
- */
- public function isPasswordValid($encoded, $raw, $salt)
- {
- return !$this->isPasswordTooLong($raw) && $this->comparePasswords($encoded, $this->encodePassword($raw, $salt));
- }
-}
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Security\Authentication\Provider;
-
-use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
-use Symfony\Component\Security\Core\User\UserProviderInterface;
-use Symfony\Component\Security\Core\User\UserCheckerInterface;
-use Symfony\Component\Security\Core\User\UserInterface;
-use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
-use Symfony\Component\Security\Core\Exception\AuthenticationServiceException;
-use Symfony\Component\Security\Core\Exception\BadCredentialsException;
-use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
-use Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider;
-
-class WallabagAuthenticationProvider extends UserAuthenticationProvider
-{
- private $encoderFactory;
- private $userProvider;
-
- /**
- * Constructor.
- *
- * @param UserProviderInterface $userProvider An UserProviderInterface instance
- * @param UserCheckerInterface $userChecker An UserCheckerInterface instance
- * @param string $providerKey The provider key
- * @param EncoderFactoryInterface $encoderFactory An EncoderFactoryInterface instance
- * @param bool $hideUserNotFoundExceptions Whether to hide user not found exception or not
- */
- public function __construct(UserProviderInterface $userProvider, UserCheckerInterface $userChecker, $providerKey, EncoderFactoryInterface $encoderFactory, $hideUserNotFoundExceptions = true)
- {
- parent::__construct($userChecker, $providerKey, $hideUserNotFoundExceptions);
-
- $this->encoderFactory = $encoderFactory;
- $this->userProvider = $userProvider;
- }
-
- /**
- * {@inheritdoc}
- */
- protected function checkAuthentication(UserInterface $user, UsernamePasswordToken $token)
- {
- $currentUser = $token->getUser();
- if ($currentUser instanceof UserInterface) {
- if ($currentUser->getPassword() !== $user->getPassword()) {
- throw new BadCredentialsException('The credentials were changed from another session.');
- }
- } else {
- if ('' === ($presentedPassword = $token->getCredentials())) {
- throw new BadCredentialsException('The presented password cannot be empty.');
- }
-
- // give username, it's used to hash the password
- $encoder = $this->encoderFactory->getEncoder($user);
- $encoder->setUsername($user->getUsername());
-
- if (!$encoder->isPasswordValid($user->getPassword(), $presentedPassword, $user->getSalt())) {
- throw new BadCredentialsException('The presented password is invalid.');
- }
- }
- }
-
- /**
- * {@inheritdoc}
- */
- protected function retrieveUser($username, UsernamePasswordToken $token)
- {
- $user = $token->getUser();
- if ($user instanceof UserInterface) {
- return $user;
- }
-
- try {
- $user = $this->userProvider->loadUserByUsername($username);
-
- if (!$user instanceof UserInterface) {
- throw new AuthenticationServiceException('The user provider must return a UserInterface object.');
- }
-
- return $user;
- } catch (UsernameNotFoundException $notFound) {
- $notFound->setUsername($username);
- throw $notFound;
- } catch (\Exception $repositoryProblem) {
- $ex = new AuthenticationServiceException($repositoryProblem->getMessage(), 0, $repositoryProblem);
- $ex->setToken($token);
- throw $ex;
- }
- }
-}
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Security\Validator;
-
-use Symfony\Component\Security\Core\User\UserInterface;
-use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
-use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
-use Symfony\Component\Validator\Constraint;
-use Symfony\Component\Validator\ConstraintValidator;
-use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
-use Symfony\Component\Validator\Exception\UnexpectedTypeException;
-use Symfony\Component\Security\Core\Validator\Constraints\UserPassword;
-
-/**
- * @see Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator
- */
-class WallabagUserPasswordValidator extends ConstraintValidator
-{
- private $securityContext;
- private $encoderFactory;
-
- public function __construct(TokenStorageInterface $tokenStorage, EncoderFactoryInterface $encoderFactory)
- {
- $this->tokenStorage = $tokenStorage;
- $this->encoderFactory = $encoderFactory;
- }
-
- /**
- * {@inheritdoc}
- */
- public function validate($password, Constraint $constraint)
- {
- if (!$constraint instanceof UserPassword) {
- throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UserPassword');
- }
-
- $user = $this->tokenStorage->getToken()->getUser();
-
- if (!$user instanceof UserInterface) {
- throw new ConstraintDefinitionException('The User object must implement the UserInterface interface.');
- }
-
- // give username, it's used to hash the password
- $encoder = $this->encoderFactory->getEncoder($user);
- $encoder->setUsername($user->getUsername());
-
- if (!$encoder->isPasswordValid($user->getPassword(), $password, $user->getSalt())) {
- $this->context->addViolation($constraint->message);
- }
- }
-}
array(
array(
'new_user[username]' => '',
- 'new_user[password]' => '',
+ 'new_user[plainPassword][first]' => '',
+ 'new_user[plainPassword][second]' => '',
'new_user[email]' => '',
),
'Please enter a username',
array(
array(
'new_user[username]' => 'a',
- 'new_user[password]' => 'mypassword',
+ 'new_user[plainPassword][first]' => 'mypassword',
+ 'new_user[plainPassword][second]' => 'mypassword',
'new_user[email]' => '',
),
'The username is too short',
array(
array(
'new_user[username]' => 'wallace',
- 'new_user[password]' => 'mypassword',
+ 'new_user[plainPassword][first]' => 'mypassword',
+ 'new_user[plainPassword][second]' => 'mypassword',
'new_user[email]' => 'test',
),
'The email is not valid',
array(
array(
'new_user[username]' => 'admin',
- 'new_user[password]' => 'wallacewallace',
+ 'new_user[plainPassword][first]' => 'wallacewallace',
+ 'new_user[plainPassword][second]' => 'wallacewallace',
'new_user[email]' => 'wallace@wallace.me',
),
'The username is already used',
),
+ array(
+ array(
+ 'new_user[username]' => 'wallace',
+ 'new_user[plainPassword][first]' => 'mypassword1',
+ 'new_user[plainPassword][second]' => 'mypassword2',
+ 'new_user[email]' => 'wallace@wallace.me',
+ ),
+ 'This value is not valid',
+ ),
);
}
$data = array(
'new_user[username]' => 'wallace',
- 'new_user[password]' => 'wallace1',
+ 'new_user[plainPassword][first]' => 'wallace1',
+ 'new_user[plainPassword][second]' => 'wallace1',
'new_user[email]' => 'wallace@wallace.me',
);
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
- ->getRepository('WallabagCoreBundle:User')
+ ->getRepository('WallabagUserBundle:User')
->findOneByUsername('wallace');
$this->assertTrue(false !== $user);
// reset the token
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
- ->getRepository('WallabagCoreBundle:User')
+ ->getRepository('WallabagUserBundle:User')
->findOneByUsername('admin');
if (!$user) {
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
- ->getRepository('WallabagCoreBundle:User')
+ ->getRepository('WallabagUserBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
- ->getRepository('WallabagCoreBundle:User')
+ ->getRepository('WallabagUserBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
$client = $this->getClient();
$em = $client->getContainer()->get('doctrine.orm.entity_manager');
$user = $em
- ->getRepository('WallabagCoreBundle:User')
+ ->getRepository('WallabagUserBundle:User')
->findOneByUsername('admin');
$config = $user->getConfig();
+++ /dev/null
-<?php
-
-namespace Wallabag\CoreBundle\Tests\Controller;
-
-use Symfony\Component\Filesystem\Filesystem;
-use Symfony\Component\Finder\Finder;
-use Wallabag\CoreBundle\Tests\WallabagCoreTestCase;
-
-class SecurityControllerTest extends WallabagCoreTestCase
-{
- public function testLogin()
- {
- $client = $this->getClient();
-
- $crawler = $client->request('GET', '/new');
-
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
- $this->assertContains('login', $client->getResponse()->headers->get('location'));
- }
-
- public function testLoginFail()
- {
- $client = $this->getClient();
-
- $crawler = $client->request('GET', '/login');
-
- $form = $crawler->filter('button[type=submit]')->form();
- $data = array(
- '_username' => 'admin',
- '_password' => 'admin',
- );
-
- $client->submit($form, $data);
-
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
- $this->assertContains('login', $client->getResponse()->headers->get('location'));
-
- $crawler = $client->followRedirect();
-
- $this->assertContains('Bad credentials', $client->getResponse()->getContent());
- }
-
- public function testRedirectionAfterLogin()
- {
- $client = $this->getClient();
- $client->followRedirects();
-
- $crawler = $client->request('GET', '/config');
-
- $form = $crawler->filter('button[type=submit]')->form();
-
- $data = array(
- '_username' => 'admin',
- '_password' => 'mypassword',
- );
-
- $client->submit($form, $data);
-
- $this->assertContains('RSS', $client->getResponse()->getContent());
- }
-
- public function testForgotPassword()
- {
- $client = $this->getClient();
-
- $crawler = $client->request('GET', '/forgot-password');
-
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
-
- $this->assertContains('Forgot password', $client->getResponse()->getContent());
-
- $form = $crawler->filter('button[type=submit]');
-
- $this->assertCount(1, $form);
-
- return array(
- 'form' => $form->form(),
- 'client' => $client,
- );
- }
-
- /**
- * @depends testForgotPassword
- */
- public function testSubmitForgotPasswordFail($parameters)
- {
- $form = $parameters['form'];
- $client = $parameters['client'];
-
- $data = array(
- 'forgot_password[email]' => 'material',
- );
-
- $client->submit($form, $data);
-
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
- $this->assertContains('No user found with this email', $client->getResponse()->getContent());
- }
-
- /**
- * @depends testForgotPassword
- *
- * Instead of using collector which slow down the test suite
- * http://symfony.com/doc/current/cookbook/email/testing.html
- *
- * Use a different way where Swift store email as file
- */
- public function testSubmitForgotPassword($parameters)
- {
- $form = $parameters['form'];
- $client = $parameters['client'];
-
- $spoolDir = $client->getKernel()->getContainer()->getParameter('swiftmailer.spool.default.file.path');
-
- // cleanup pool dir
- $filesystem = new Filesystem();
- $filesystem->remove($spoolDir);
-
- // to use `getCollector` since `collect: false` in config_test.yml
- $client->enableProfiler();
-
- $data = array(
- 'forgot_password[email]' => 'bobby@wallabag.org',
- );
-
- $client->submit($form, $data);
-
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
-
- $crawler = $client->followRedirect();
-
- $this->assertContains('An email has been sent to', $client->getResponse()->getContent());
-
- // find every files (ie: emails) inside the spool dir except hidden files
- $finder = new Finder();
- $finder
- ->in($spoolDir)
- ->ignoreDotFiles(true)
- ->files();
-
- $this->assertCount(1, $finder, 'Only one email has been sent');
-
- foreach ($finder as $file) {
- $message = unserialize(file_get_contents($file));
-
- $this->assertInstanceOf('Swift_Message', $message);
- $this->assertEquals('Reset Password', $message->getSubject());
- $this->assertEquals('no-reply@wallabag.org', key($message->getFrom()));
- $this->assertEquals('bobby@wallabag.org', key($message->getTo()));
- $this->assertContains(
- 'To reset your password - please visit',
- $message->getBody()
- );
- }
- }
-
- public function testReset()
- {
- $client = $this->getClient();
- $user = $client->getContainer()
- ->get('doctrine.orm.entity_manager')
- ->getRepository('WallabagCoreBundle:User')
- ->findOneByEmail('bobby@wallabag.org');
-
- $crawler = $client->request('GET', '/forgot-password/'.$user->getConfirmationToken());
-
- $this->assertEquals(200, $client->getResponse()->getStatusCode());
- $this->assertCount(2, $crawler->filter('input[type=password]'));
- $this->assertCount(1, $form = $crawler->filter('button[type=submit]'));
- $this->assertCount(1, $form);
-
- $data = array(
- 'change_passwd[new_password][first]' => 'mypassword',
- 'change_passwd[new_password][second]' => 'mypassword',
- );
-
- $client->submit($form->form(), $data);
-
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
- $this->assertContains('login', $client->getResponse()->headers->get('location'));
- }
-
- public function testResetBadToken()
- {
- $client = $this->getClient();
-
- $client->request('GET', '/forgot-password/UIZOAU29UE902IEPZO');
-
- $this->assertEquals(404, $client->getResponse()->getStatusCode());
- }
-
- public function testCheckEmailWithoutEmail()
- {
- $client = $this->getClient();
-
- $client->request('GET', '/forgot-password/check-email');
-
- $this->assertEquals(302, $client->getResponse()->getStatusCode());
- $this->assertContains('forgot-password', $client->getResponse()->headers->get('location'));
- }
-}
--- /dev/null
+<?php
+
+namespace Wallabag\CoreBundle\Tests\EventListener;
+
+use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
+use Symfony\Component\EventDispatcher\EventDispatcher;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use FOS\UserBundle\FOSUserEvents;
+use FOS\UserBundle\Event\FilterUserResponseEvent;
+use Wallabag\CoreBundle\EventListener\RegistrationConfirmedListener;
+use Wallabag\CoreBundle\Entity\Config;
+use Wallabag\UserBundle\Entity\User;
+
+class RegistrationConfirmedListenerTest extends KernelTestCase
+{
+ private $em;
+ private $listener;
+ private $dispatcher;
+ private $request;
+ private $response;
+
+ protected function setUp()
+ {
+ $this->em = $this->getMockBuilder('Doctrine\ORM\EntityManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->listener = new RegistrationConfirmedListener(
+ $this->em,
+ 'baggy',
+ 20,
+ 50,
+ 'fr'
+ );
+
+ $this->dispatcher = new EventDispatcher();
+ $this->dispatcher->addSubscriber($this->listener);
+
+ $this->request = Request::create('/');
+ $this->response = Response::create();
+ }
+
+ public function testWithInvalidUser()
+ {
+ $user = new User();
+ $user->setEnabled(false);
+
+ $event = new FilterUserResponseEvent(
+ $user,
+ $this->request,
+ $this->response
+ );
+
+ $this->em->expects($this->never())->method('persist');
+ $this->em->expects($this->never())->method('flush');
+
+ $this->dispatcher->dispatch(
+ FOSUserEvents::REGISTRATION_CONFIRMED,
+ $event
+ );
+ }
+
+ public function testWithValidUser()
+ {
+ $user = new User();
+ $user->setEnabled(true);
+
+ $event = new FilterUserResponseEvent(
+ $user,
+ $this->request,
+ $this->response
+ );
+
+ $config = new Config($user);
+ $config->setTheme('baggy');
+ $config->setItemsPerPage(20);
+ $config->setRssLimit(50);
+ $config->setLanguage('fr');
+
+ $this->em->expects($this->once())
+ ->method('persist')
+ ->will($this->returnValue($config));
+ $this->em->expects($this->once())
+ ->method('flush');
+
+ $this->dispatcher->dispatch(
+ FOSUserEvents::REGISTRATION_CONFIRMED,
+ $event
+ );
+ }
+}
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Wallabag\CoreBundle\Entity\Entry;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
use Wallabag\CoreBundle\Helper\ContentProxy;
class ContentProxyTest extends KernelTestCase
use Wallabag\CoreBundle\ParamConverter\UsernameRssTokenConverter;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\HttpFoundation\Request;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
class UsernameRssTokenConverterTest extends KernelTestCase
{
$meta->expects($this->once())
->method('getName')
- ->will($this->returnValue('Wallabag\CoreBundle\Entity\User'));
+ ->will($this->returnValue('Wallabag\UserBundle\Entity\User'));
$em = $this->getMockBuilder('Doctrine\Common\Persistence\ObjectManager')
->disableOriginalConstructor()
$em->expects($this->once())
->method('getClassMetadata')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($meta));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
$registry->expects($this->once())
->method('getManagerForClass')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($em));
- $params = new ParamConverter(array('class' => 'WallabagCoreBundle:User'));
+ $params = new ParamConverter(array('class' => 'WallabagUserBundle:User'));
$converter = new UsernameRssTokenConverter($registry);
$this->assertTrue($converter->supports($params));
*/
public function testApplyUserNotFound()
{
- $repo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\UserRepository')
+ $repo = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getRepository')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($repo));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
$registry->expects($this->once())
->method('getManagerForClass')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($em));
- $params = new ParamConverter(array('class' => 'WallabagCoreBundle:User'));
+ $params = new ParamConverter(array('class' => 'WallabagUserBundle:User'));
$converter = new UsernameRssTokenConverter($registry);
$request = new Request(array(), array(), array('username' => 'test', 'token' => 'test'));
{
$user = new User();
- $repo = $this->getMockBuilder('Wallabag\CoreBundle\Repository\UserRepository')
+ $repo = $this->getMockBuilder('Wallabag\UserBundle\Repository\UserRepository')
->disableOriginalConstructor()
->getMock();
$em->expects($this->once())
->method('getRepository')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($repo));
$registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
$registry->expects($this->once())
->method('getManagerForClass')
- ->with('WallabagCoreBundle:User')
+ ->with('WallabagUserBundle:User')
->will($this->returnValue($em));
- $params = new ParamConverter(array('class' => 'WallabagCoreBundle:User', 'name' => 'user'));
+ $params = new ParamConverter(array('class' => 'WallabagUserBundle:User', 'name' => 'user'));
$converter = new UsernameRssTokenConverter($registry);
$request = new Request(array(), array(), array('username' => 'test', 'token' => 'test'));
--- /dev/null
+<?php
+
+namespace Wallabag\UserBundle\Controller;
+
+use FOS\UserBundle\FOSUserEvents;
+use FOS\UserBundle\Event\FormEvent;
+use FOS\UserBundle\Event\GetResponseUserEvent;
+use FOS\UserBundle\Event\FilterUserResponseEvent;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+class ResettingController extends \FOS\UserBundle\Controller\ResettingController
+{
+ /**
+ * Extends ResettingController to change the redirection after success.
+ *
+ * @param Request $request
+ * @param $token
+ *
+ * @return null|RedirectResponse|\Symfony\Component\HttpFoundation\Response
+ */
+ public function resetAction(Request $request, $token)
+ {
+ /** @var $formFactory \FOS\UserBundle\Form\Factory\FactoryInterface */
+ $formFactory = $this->get('fos_user.resetting.form.factory');
+ /** @var $userManager \FOS\UserBundle\Model\UserManagerInterface */
+ $userManager = $this->get('fos_user.user_manager');
+ /** @var $dispatcher \Symfony\Component\EventDispatcher\EventDispatcherInterface */
+ $dispatcher = $this->get('event_dispatcher');
+
+ $user = $userManager->findUserByConfirmationToken($token);
+
+ if (null === $user) {
+ throw new NotFoundHttpException(sprintf('The user with "confirmation token" does not exist for value "%s"', $token));
+ }
+
+ $event = new GetResponseUserEvent($user, $request);
+ $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_INITIALIZE, $event);
+
+ if (null !== $event->getResponse()) {
+ return $event->getResponse();
+ }
+
+ $form = $formFactory->createForm();
+ $form->setData($user);
+
+ $form->handleRequest($request);
+
+ if ($form->isValid()) {
+ $event = new FormEvent($form, $request);
+ $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_SUCCESS, $event);
+
+ $userManager->updateUser($user);
+
+ if (null === $response = $event->getResponse()) {
+ $this->get('session')->getFlashBag()->add(
+ 'notice',
+ 'Password updated'
+ );
+ $url = $this->generateUrl('homepage');
+ $response = new RedirectResponse($url);
+ }
+
+ $dispatcher->dispatch(FOSUserEvents::RESETTING_RESET_COMPLETED, new FilterUserResponseEvent($user, $request, $response));
+
+ return $response;
+ }
+
+ return $this->render('FOSUserBundle:Resetting:reset.html.twig', array(
+ 'token' => $token,
+ 'form' => $form->createView(),
+ ));
+ }
+}
<?php
-namespace Wallabag\CoreBundle\DataFixtures\ORM;
+namespace Wallabag\UserBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
-use Wallabag\CoreBundle\Entity\User;
+use Wallabag\UserBundle\Entity\User;
class LoadUserData extends AbstractFixture implements OrderedFixtureInterface
{
$userAdmin->setName('Big boss');
$userAdmin->setEmail('bigboss@wallabag.org');
$userAdmin->setUsername('admin');
- $userAdmin->setPassword('mypassword');
+ $userAdmin->setPlainPassword('mypassword');
$userAdmin->setEnabled(true);
+ $userAdmin->addRole('ROLE_SUPER_ADMIN');
$manager->persist($userAdmin);
$bobUser->setName('Bobby');
$bobUser->setEmail('bobby@wallabag.org');
$bobUser->setUsername('bob');
- $bobUser->setPassword('mypassword');
+ $bobUser->setPlainPassword('mypassword');
$bobUser->setEnabled(true);
$manager->persist($bobUser);
<?php
-namespace Wallabag\CoreBundle\Entity;
+namespace Wallabag\UserBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
-use Symfony\Component\Security\Core\User\AdvancedUserInterface;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Expose;
use FOS\UserBundle\Model\User as BaseUser;
+use Wallabag\CoreBundle\Entity\Config;
+use Wallabag\CoreBundle\Entity\Entry;
+use Wallabag\CoreBundle\Entity\Tag;
/**
* User.
*
- * @ORM\Entity(repositoryClass="Wallabag\CoreBundle\Repository\UserRepository")
+ * @ORM\Entity(repositoryClass="Wallabag\UserBundle\Repository\UserRepository")
* @ORM\Table
* @ORM\HasLifecycleCallbacks()
* @ExclusionPolicy("all")
* @UniqueEntity("email")
* @UniqueEntity("username")
*/
-class User extends BaseUser implements AdvancedUserInterface, \Serializable
+class User extends BaseUser
{
/**
* @var int
protected $updatedAt;
/**
- * @ORM\OneToMany(targetEntity="Entry", mappedBy="user", cascade={"remove"})
+ * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Entry", mappedBy="user", cascade={"remove"})
*/
protected $entries;
/**
- * @ORM\OneToOne(targetEntity="Config", mappedBy="user")
+ * @ORM\OneToOne(targetEntity="Wallabag\CoreBundle\Entity\Config", mappedBy="user")
*/
protected $config;
/**
- * @ORM\OneToMany(targetEntity="Tag", mappedBy="user", cascade={"remove"})
+ * @ORM\OneToMany(targetEntity="Wallabag\CoreBundle\Entity\Tag", mappedBy="user", cascade={"remove"})
*/
protected $tags;
parent::__construct();
$this->entries = new ArrayCollection();
$this->tags = new ArrayCollection();
+ $this->roles = array('ROLE_USER');
}
/**
$this->updatedAt = new \DateTime();
}
- /**
- * Set password.
- *
- * @param string $password
- *
- * @return User
- */
- public function setPassword($password)
- {
- if (!$password && 0 === strlen($password)) {
- return;
- }
-
- $this->password = sha1($password.$this->getUsername().$this->getSalt());
-
- return $this;
- }
-
/**
* Set name.
*
/**
* Set config.
*
- * @param \Wallabag\CoreBundle\Entity\Config $config
+ * @param Config $config
*
* @return User
*/
- public function setConfig(\Wallabag\CoreBundle\Entity\Config $config = null)
+ public function setConfig(Config $config = null)
{
$this->config = $config;
/**
* Get config.
*
- * @return \Wallabag\CoreBundle\Entity\Config
+ * @return Config
*/
public function getConfig()
{
<?php
-namespace Wallabag\CoreBundle\Repository;
+namespace Wallabag\UserBundle\Repository;
use Doctrine\ORM\EntityRepository;
--- /dev/null
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}{% trans %}create an account{% endtrans %}{% endblock %}
+
+{% block body_class %}login{% endblock %}
+
+{% block menu %}{% endblock %}
+{% block messages %}{% endblock %}
+
+{% block content %}
+ <form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register">
+ <fieldset class="w500p center">
+ <h2 class="mbs txtcenter">{% trans %}create an account{% endtrans %}</h2>
+ {% include "FOSUserBundle:Registration:register_content.html.twig" %}
+ </fieldset>
+ </form>
+{% endblock %}
+
+{% block footer %}
+{% endblock %}
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+
+{{ form_widget(form._token) }}
+
+{% for flashMessage in app.session.flashbag.get('notice') %}
+ <span><p>{{ flashMessage }}</p></span>
+{% endfor %}
+
+<div class="row">
+ {{ form_errors(form.email) }}
+ {{ form_label(form.email) }}
+ {{ form_widget(form.email) }}
+</div>
+
+<div class="row">
+ {{ form_errors(form.username) }}
+ {{ form_label(form.username) }}
+ {{ form_widget(form.username) }}
+</div>
+
+<div class="row">
+ {{ form_errors(form.plainPassword.first) }}
+ {{ form_label(form.plainPassword.first) }}
+ {{ form_widget(form.plainPassword.first) }}
+</div>
+
+<div class="row">
+ {{ form_errors(form.plainPassword.second) }}
+ {{ form_label(form.plainPassword.second) }}
+ {{ form_widget(form.plainPassword.second) }}
+</div>
+
+
+<div class="row mts txtcenter">
+ <button type="submit">{{ 'registration.submit'|trans({}, 'FOSUserBundle') }}</button>
+ <a href="{{ path('fos_user_security_login') }}" class="button">{% trans %}Login{% endtrans %}</a>
+</div>
--- /dev/null
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}{% trans %}Forgot password{% endtrans %}{% endblock %}
+
+{% block body_class %}login{% endblock %}
+
+{% block menu %}{% endblock %}
+{% block messages %}{% endblock %}
+
+{% block content %}
+ <form action="{{ path('fos_user_resetting_send_email') }}" method="post" name="forgotPasswordform">
+ <fieldset class="w500p center">
+ <h2 class="mbs txtcenter">{% trans %}Forgot password{% endtrans %}</h2>
+ {% include "FOSUserBundle:Resetting:request_content.html.twig" %}
+ </fieldset>
+ </form>
+{% endblock %}
+
+{% block footer %}
+{% endblock %}
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% trans %}Enter your email address below and we'll send you password reset instructions.{% endtrans %}
+
+{% if invalid_username is defined %}
+ <p>{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}) }}</p>
+{% endif %}
+
+<div class="row">
+ <label for="username">{{ 'resetting.request.username'|trans }}</label>
+ <input type="text" id="username" name="username" required="required" />
+</div>
+
+<div class="row mts txtcenter">
+ <button type="submit">{{ 'resetting.request.submit'|trans }}</button>
+ <a href="{{ path('fos_user_security_login') }}" class="button">{% trans %}Login{% endtrans %}</a>
+</div>
-{% extends "WallabagCoreBundle::layout.html.twig" %}
+{% extends "FOSUserBundle::layout.html.twig" %}
-{% block title %}{% trans %}login to your wallabag{% endtrans %}{% endblock %}
-
-{% block body_class %}login{% endblock %}
-
-{% block menu %}{% endblock %}
-{% block messages %}{% endblock %}
-
-{% block content %}
- <form action="{{ path('login_check') }}" method="post" name="loginform">
+{% block fos_user_content %}
+ <form action="{{ path('fos_user_security_check') }}" method="post" name="loginform">
<fieldset class="w500p center">
<h2 class="mbs txtcenter">{% trans %}Login to wallabag{% endtrans %}</h2>
{% if error %}
<div class="row mts txtcenter">
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}" />
<button type="submit">Login</button>
- <a href="{{ path('forgot_password') }}" class="small">Forgot your password?</a>
+ <a href="{{ path('fos_user_registration_register') }}" class="button">{% trans %}Register{% endtrans %}</a>
+ <a href="{{ path('fos_user_resetting_request') }}" class="small">Forgot your password?</a>
</div>
</fieldset>
</form>
--- /dev/null
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}Welcome on wallabag!{% endblock %}
+
+{% block body_class %}login{% endblock %}
+
+{% block menu %}{% endblock %}
+{% block messages %}{% endblock %}
+
+{% block content %}
+ {% block fos_user_content %}
+ {% endblock fos_user_content %}
+{% endblock %}
+
+{% block footer %}
+{% endblock %}
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+
+<form action="{{ path('fos_user_change_password') }}" {{ form_enctype(form) }} method="POST" class="fos_user_change_password">
+ <div class="card-content">
+ <div class="row">
+ {{ form_widget(form) }}
+ <div>
+ <input type="submit" value="{{ 'change_password.submit'|trans }}" />
+ </div>
+ </div>
+ </div>
+</form>
--- /dev/null
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_content %}
+<div class="card-content">
+ <div class="row">
+ <p>{{ 'registration.check_email'|trans({'%email%': user.email}) }}</p>
+ </div>
+</div>
+{% endblock fos_user_content %}
--- /dev/null
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_content %}
+<div class="card-content">
+ <div class="row">
+ <p>{{ 'registration.confirmed'|trans({'%username%': user.username}) }}</p>
+ {% if targetUrl %}
+ <p><a href="{{ targetUrl }}">{{ 'registration.back'|trans }}</a></p>
+ {% endif %}
+ </div>
+ <div class="card-action center">
+ <a href="{{ path('homepage') }}" class="waves-effect waves-light btn"><i class="material-icons left"></i> {% trans %}Go to your account{% endtrans %}</a>
+ </div>
+</div>
+{% endblock fos_user_content %}
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+
+<form action="{{ path('fos_user_registration_register') }}" {{ form_enctype(form) }} method="POST" class="fos_user_registration_register">
+ <div class="card-content">
+ <div class="row">
+
+ {{ form_widget(form._token) }}
+
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ <span class="black-text"><p>{{ flashMessage }}</p></span>
+ {% endfor %}
+
+ <div class="input-field col s12">
+ {{ form_errors(form.email) }}
+ {{ form_label(form.email) }}
+ {{ form_widget(form.email) }}
+ </div>
+
+ <div class="input-field col s12">
+ {{ form_errors(form.username) }}
+ {{ form_label(form.username) }}
+ {{ form_widget(form.username) }}
+ </div>
+
+ <div class="input-field col s12">
+ {{ form_errors(form.plainPassword.first) }}
+ {{ form_label(form.plainPassword.first) }}
+ {{ form_widget(form.plainPassword.first) }}
+ </div>
+
+ <div class="input-field col s12">
+ {{ form_errors(form.plainPassword.second) }}
+ {{ form_label(form.plainPassword.second) }}
+ {{ form_widget(form.plainPassword.second) }}
+ </div>
+ </div>
+ </div>
+ <div class="card-action center">
+ <a href="{{ path('fos_user_security_login') }}" class="waves-effect waves-light grey btn"><i class="material-icons left"></i> {% trans %}Login{% endtrans %}</a>
+ <button class="btn waves-effect waves-light" type="submit" name="send">
+ {{ 'registration.submit'|trans({}, 'FOSUserBundle') }}
+ <i class="mdi-content-send right"></i>
+ </button>
+ </div>
+</form>
--- /dev/null
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_content %}
+<div class="card-content">
+ <div class="row">
+ {{ 'resetting.check_email'|trans({'%email%': email}) }}
+ </div>
+</div>
+{% endblock fos_user_content %}
--- /dev/null
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% trans_default_domain 'FOSUserBundle' %}
+
+{% block fos_user_content %}
+<div class="card-content">
+ <div class="row">
+ {{ 'resetting.password_already_requested'|trans }}
+ </div>
+</div>
+{% endblock fos_user_content %}
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+<form action="{{ path('fos_user_resetting_send_email') }}" method="POST" class="fos_user_resetting_request">
+ <div class="card-content">
+ <div class="row">
+ <p>{% trans %}Enter your email address below and we'll send you password reset instructions.{% endtrans %}</p>
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ <span class="black-text"><p>{{ flashMessage }}</p></span>
+ {% endfor %}
+
+ {% if invalid_username is defined %}
+ <p>{{ 'resetting.request.invalid_username'|trans({'%username%': invalid_username}) }}</p>
+ {% endif %}
+
+ <div class="input-field col s12">
+ <label for="username">{{ 'resetting.request.username'|trans }}</label>
+ <input type="text" id="username" name="username" required="required" />
+ </div>
+ </div>
+ </div>
+ <div class="card-action center">
+ <a href="{{ path('fos_user_security_login') }}" class="waves-effect waves-light grey btn"><i class="material-icons left"></i> {% trans %}Login{% endtrans %}</a>
+ <button class="btn waves-effect waves-light" type="submit" name="send">
+ {{ 'resetting.request.submit'|trans }}
+ </button>
+ </div>
+</form>
--- /dev/null
+{% trans_default_domain 'FOSUserBundle' %}
+
+<form action="{{ path('fos_user_resetting_reset', {'token': token}) }}" {{ form_enctype(form) }} method="POST" class="fos_user_resetting_reset">
+ <div class="card-content">
+ <div class="row">
+ {{ form_widget(form) }}
+ </div>
+ <div class="card-action center">
+ <button class="btn waves-effect waves-light" type="submit" name="send">
+ {{ 'resetting.reset.submit'|trans }}
+ <i class="mdi-content-send right"></i>
+ </button>
+ </div>
+ </div>
+</form>
--- /dev/null
+{% extends "FOSUserBundle::layout.html.twig" %}
+
+{% block fos_user_content %}
+<form action="{{ path('fos_user_security_check') }}" method="post" name="loginform">
+ <div class="card-content">
+
+ {% if error %}
+ <span class="black-text">{{ error.message }}</span>
+ {% endif %}
+
+ {% for flashMessage in app.session.flashbag.get('notice') %}
+ <span class="black-text"><p>{{ flashMessage }}</p></span>
+ {% endfor %}
+
+ <div class="row">
+
+ <div class="input-field col s12">
+ <label for="username">{% trans %}Username{% endtrans %}</label>
+ <input type="text" id="username" name="_username" value="{{ last_username }}" />
+ </div>
+
+ <div class="input-field col s12">
+ <label for="password">{% trans %}Password{% endtrans %}</label>
+ <input type="password" id="password" name="_password" />
+ </div>
+
+ <div class="input-field col s12">
+ <input type="checkbox" id="remember_me" name="_remember_me" checked />
+ <label for="remember_me">{% trans %}Keep me logged in{% endtrans %}</label>
+ </div>
+
+ </div>
+ </div>
+ <div class="card-action center">
+ <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}" />
+ <a href="{{ path('fos_user_registration_register') }}" class="waves-effect waves-light grey btn"><i class="material-icons left"></i> {% trans %}Register{% endtrans %}</a>
+ <button class="btn waves-effect waves-light" type="submit" name="send">
+ {% trans %}Login{% endtrans %}
+ <i class="mdi-content-send right"></i>
+ </button>
+ </div>
+ <div class="center">
+ <a href="{{ path('fos_user_resetting_request') }}">{% trans %}Forgot your password?{% endtrans %}</a>
+ </div>
+</form>
+{% endblock fos_user_content %}
--- /dev/null
+{% extends "WallabagCoreBundle::layout.html.twig" %}
+
+{% block title %}Welcome on wallabag!{% endblock %}
+
+{% block body_class %}login{% endblock %}
+
+{% block menu %}{% endblock %}
+{% block messages %}{% endblock %}
+
+{% block content %}
+<main class="valign-wrapper">
+ <div class="valign row">
+ <div class="card sw">
+ <div class="center"><img src="{{ asset('themes/material/img/logo-other_themes.png') }}" alt="wallabag logo" /></div>
+ {% block fos_user_content %}
+ {% endblock fos_user_content %}
+ </div>
+ </div>
+</main>
+{% endblock %}
+
+{% block footer %}
+{% endblock %}
--- /dev/null
+<?php
+
+namespace Wallabag\UserBundle;
+
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+class WallabagUserBundle extends Bundle
+{
+ public function getParent()
+ {
+ return 'FOSUserBundle';
+ }
+}