2020-07-22 10:58:25 +09:00
< ? php
namespace App\Controller ;
2020-07-23 23:08:31 +09:00
use App\Core\Controller ;
2020-07-25 11:06:55 +09:00
use App\Core\DB\DB ;
use App\Core\Form ;
use function App\Core\I18n\_m ;
2021-07-29 06:38:27 +09:00
use App\Core\Log ;
2021-04-16 07:26:05 +09:00
use App\Core\VisibilityScope ;
2020-10-12 05:27:50 +09:00
use App\Entity\Follow ;
2020-08-13 10:23:22 +09:00
use App\Entity\GSActor ;
2020-07-25 11:06:55 +09:00
use App\Entity\LocalUser ;
2020-12-03 07:57:32 +09:00
use App\Entity\Note ;
2020-07-25 11:06:55 +09:00
use App\Security\Authenticator ;
use app\Util\Common ;
2021-07-29 06:38:27 +09:00
use App\Util\Exception\EmailTakenException ;
2021-04-11 20:03:32 +09:00
use App\Util\Exception\NicknameTakenException ;
2021-07-29 06:38:27 +09:00
use App\Util\Exception\ServerException ;
2020-07-25 11:06:55 +09:00
use App\Util\Nickname ;
2021-04-11 20:03:32 +09:00
use Doctrine\DBAL\Exception\UniqueConstraintViolationException ;
2020-07-25 11:06:55 +09:00
use Symfony\Component\Form\Extension\Core\Type\EmailType ;
use Symfony\Component\Form\Extension\Core\Type\PasswordType ;
use Symfony\Component\Form\Extension\Core\Type\SubmitType ;
use Symfony\Component\Form\Extension\Core\Type\TextType ;
use Symfony\Component\HttpFoundation\Request ;
use Symfony\Component\Security\Guard\GuardAuthenticatorHandler ;
2020-07-22 10:58:25 +09:00
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils ;
2020-07-25 11:06:55 +09:00
use Symfony\Component\Validator\Constraints\Length ;
use Symfony\Component\Validator\Constraints\NotBlank ;
2020-07-22 10:58:25 +09:00
2020-07-23 23:08:31 +09:00
class Security extends Controller
2020-07-22 10:58:25 +09:00
{
2020-11-07 04:47:15 +09:00
/**
* Log a user in
*/
2020-07-24 02:55:06 +09:00
public function login ( AuthenticationUtils $authenticationUtils )
2020-07-22 10:58:25 +09:00
{
if ( $this -> getUser ()) {
return $this -> redirectToRoute ( 'main_all' );
}
// get the login error if there is one
$error = $authenticationUtils -> getLastAuthenticationError ();
// last username entered by the user
2021-07-29 06:38:27 +09:00
$last_login_id = $authenticationUtils -> getLastUsername ();
2020-07-22 10:58:25 +09:00
2021-07-29 06:38:27 +09:00
return [
'_template' => 'security/login.html.twig' ,
'last_login_id' => $last_login_id ,
'error' => $error ,
'notes_fn' => fn () => Note :: getAllNotes ( VisibilityScope :: $instance_scope ),
];
2020-07-22 10:58:25 +09:00
}
2021-07-29 06:38:27 +09:00
/**
* @ codeCoverageIgnore
*/
2020-07-22 10:58:25 +09:00
public function logout ()
{
throw new \LogicException ( 'This method can be blank - it will be intercepted by the logout key on your firewall.' );
}
2020-07-25 11:06:55 +09:00
2020-11-07 04:47:15 +09:00
/**
* Register a user , making sure the nickname is not reserved and
* possibly sending a confirmation email
*/
2020-07-25 11:06:55 +09:00
public function register ( Request $request ,
GuardAuthenticatorHandler $guard_handler ,
Authenticator $authenticator )
{
$form = Form :: create ([
[ 'nickname' , TextType :: class , [
'label' => _m ( 'Nickname' ),
2021-07-29 06:06:10 +09:00
'constraints' => [
new NotBlank ([ 'message' => _m ( 'Please enter a nickname' )]),
new Length ([
2021-07-29 06:38:27 +09:00
'min' => Common :: config ( 'nickname' , 'min_length' ),
'minMessage' => _m ([ 'Your nickname must be at least # characters long' ], [ 'count' => Common :: config ( 'nickname' , 'min_length' )]),
'max' => Nickname :: MAX_LEN ,
'maxMessage' => _m ([ 'Your nickname must be at most # characters long' ], [ 'count' => Nickname :: MAX_LEN ]), ]),
2020-07-25 11:06:55 +09:00
],
]],
2021-07-29 06:06:10 +09:00
[ 'email' , EmailType :: class , [
2021-07-29 06:38:27 +09:00
'label' => _m ( 'Email' ),
'constraints' => [ new NotBlank ([ 'message' => _m ( 'Please enter an email' ) ])],
2021-07-29 06:06:10 +09:00
]],
2020-07-25 11:06:55 +09:00
[ 'password' , PasswordType :: class , [
'label' => _m ( 'Password' ),
'mapped' => false ,
'constraints' => [
new NotBlank ([ 'message' => _m ( 'Please enter a password' )]),
2021-07-29 06:06:10 +09:00
new Length ([ 'min' => Common :: config ( 'password' , 'min_length' ), 'minMessage' => _m ([ 'Your password should be at least # characters' ], [ 'count' => Common :: config ( 'password' , 'min_length' )]),
2021-07-29 06:38:27 +09:00
'max' => Common :: config ( 'password' , 'max_length' ), 'maxMessage' => _m ([ 'Your password should be at most # characters' ], [ 'count' => Common :: config ( 'password' , 'max_length' )]), ]),
2020-07-25 11:06:55 +09:00
],
]],
[ 'register' , SubmitType :: class , [ 'label' => _m ( 'Register' )]],
]);
$form -> handleRequest ( $request );
if ( $form -> isSubmitted () && $form -> isValid ()) {
$data = $form -> getData ();
$data [ 'password' ] = $form -> get ( 'password' ) -> getData ();
2021-07-29 06:38:27 +09:00
// This will throw the appropriate errors, result ignored
$user = LocalUser :: findByNicknameOrEmail ( $data [ 'nickname' ], $data [ 'email' ]);
if ( $user !== null ) {
// If we do find something, there's a duplicate
if ( $user -> getNickname () == $data [ 'nickname' ]) {
throw new NicknameTakenException ;
} else {
throw new EmailTakenException ;
}
}
$valid_nickname = Nickname :: validate ( $data [ 'nickname' ], check_already_used : false );
2020-07-25 11:06:55 +09:00
2021-04-11 20:03:32 +09:00
try {
2021-07-29 06:38:27 +09:00
// This already checks if the nickname is being used
$actor = GSActor :: create ([ 'nickname' => $valid_nickname ]);
2021-04-23 21:54:25 +09:00
$user = LocalUser :: create ([
2021-07-29 06:38:27 +09:00
'nickname' => $valid_nickname ,
2021-04-11 20:03:32 +09:00
'outgoing_email' => $data [ 'email' ],
'incoming_email' => $data [ 'email' ],
'password' => LocalUser :: hashPassword ( $data [ 'password' ]),
]);
2021-04-23 21:54:25 +09:00
DB :: persistWithSameId (
$actor ,
$user ,
// Self follow
fn ( int $id ) => DB :: persist ( Follow :: create ([ 'follower' => $id , 'followed' => $id ]))
);
DB :: flush ();
2021-04-11 20:03:32 +09:00
} catch ( UniqueConstraintViolationException $e ) {
2021-07-29 06:38:27 +09:00
// _something_ was duplicated, but since we already check if nickname is in use, we can't tell what went wrong
$e = 'An error occurred while trying to register' ;
Log :: critical ( $e . " with nickname: ' { $valid_nickname } ' and email ' { $data [ 'email' ] } ' " );
throw new ServerException ( _m ( $e ));
2021-04-11 20:03:32 +09:00
}
2020-07-25 11:06:55 +09:00
// generate a signed url and email it to the user
2021-07-30 00:03:52 +09:00
if ( $_ENV [ 'APP_ENV' ] === 'dev' || Common :: config ( 'site' , 'use_email' )) {
Common :: sendVerificationEmail ();
2020-07-25 11:06:55 +09:00
} else {
$user -> setIsEmailVerified ( true );
}
return $guard_handler -> authenticateUserAndHandleSuccess (
$user ,
$request ,
$authenticator ,
'main' // firewall name in security.yaml
);
}
return [
'_template' => 'security/register.html.twig' ,
'registration_form' => $form -> createView (),
2021-07-29 06:38:27 +09:00
'notes_fn' => fn () => Note :: getAllNotes ( VisibilityScope :: $instance_scope ),
2020-07-25 11:06:55 +09:00
];
}
2020-07-22 10:58:25 +09:00
}