[CLEAR-22] Implemented users registration, login & retrieving User object. Added integration tests for these endpoints.

This commit is contained in:
Artur Nowakowski 2019-11-30 18:19:13 +01:00
parent d74315a144
commit aa3938c404
47 changed files with 1922 additions and 851 deletions

View File

@ -9,9 +9,9 @@ LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=laravel
DB_DATABASE=clearbowl
DB_USERNAME=root
DB_PASSWORD=
DB_PASSWORD=8fcXUlCgC6JCqpoWX64
BROADCAST_DRIVER=log
CACHE_DRIVER=file

20
Jenkinsfile vendored
View File

@ -1,17 +1,15 @@
pipeline {
agent {
docker { image 'circleci/php:7.3' }
}
stages {
stage('Build'){
steps {
sh 'composer install'
sh 'cp .env.example .env && php artisan key:generate'
cleanWs()
docker.image('mysql:latest').withRun('-e "MYSQL_ROOT_PASSWORD=8fcXUlCgC6JCqpoWX64" -e "MYSQL_DATABASE=clearbowl" -e "MYSQL_USER=api" -e "MYSQL_PASSWORD=R23qarINlcALOk2nDg6LbJelaqk"') { c ->
docker.image('mysql:latest').inside("--link ${c.id}:db") {
stage("Initialize DB"){
sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done'
}
}
}
stage('Test') {
steps {
sh 'vendor/bin/phpunit'
docker.image('php:7.4').inside("--link ${c.id}:db") {
stage("Build") { sh 'composer install && mv .env.example .env'}
stage("Test") { sh 'vendor/bin/phpunit' }
}
}
}

12
app/Enums/UserRole.php Normal file
View File

@ -0,0 +1,12 @@
<?php
namespace App\Enums;
use BenSampo\Enum\Enum;
final class UserRole extends Enum
{
const USER = 0;
const ADMIN = 1;
}

View File

@ -1,39 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ConfirmsPasswords;
class ConfirmPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Confirm Password Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password confirmations and
| uses a simple trait to include the behavior. You're free to explore
| this trait and override any functions that require customization.
|
*/
use ConfirmsPasswords;
/**
* Where to redirect users when the intended url fails.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
}
}

View File

@ -1,22 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
class ForgotPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset emails and
| includes a trait which assists in sending these notifications from
| your application to your users. Feel free to explore this trait.
|
*/
use SendsPasswordResetEmails;
}

View File

@ -1,39 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class LoginController extends Controller
{
/*
|--------------------------------------------------------------------------
| Login Controller
|--------------------------------------------------------------------------
|
| This controller handles authenticating users for the application and
| redirecting them to your home screen. The controller uses a trait
| to conveniently provide its functionality to your applications.
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest')->except('logout');
}
}

View File

@ -1,72 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
class RegisterController extends Controller
{
/*
|--------------------------------------------------------------------------
| Register Controller
|--------------------------------------------------------------------------
|
| This controller handles the registration of new users as well as their
| validation and creation. By default this controller uses a trait to
| provide this functionality without requiring any additional code.
|
*/
use RegistersUsers;
/**
* Where to redirect users after registration.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('guest');
}
/**
* Get a validator for an incoming registration request.
*
* @param array $data
* @return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
'password' => ['required', 'string', 'min:8', 'confirmed'],
]);
}
/**
* Create a new user instance after a valid registration.
*
* @param array $data
* @return \App\User
*/
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
}

View File

@ -1,29 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\ResetsPasswords;
class ResetPasswordController extends Controller
{
/*
|--------------------------------------------------------------------------
| Password Reset Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling password reset requests
| and uses a simple trait to include this behavior. You're free to
| explore this trait and override any methods you wish to tweak.
|
*/
use ResetsPasswords;
/**
* Where to redirect users after resetting their password.
*
* @var string
*/
protected $redirectTo = '/home';
}

View File

@ -1,41 +0,0 @@
<?php
namespace App\Http\Controllers\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\VerifiesEmails;
class VerificationController extends Controller
{
/*
|--------------------------------------------------------------------------
| Email Verification Controller
|--------------------------------------------------------------------------
|
| This controller is responsible for handling email verification for any
| user that recently registered with the application. Emails may also
| be re-sent if the user didn't receive the original email message.
|
*/
use VerifiesEmails;
/**
* Where to redirect users after verification.
*
* @var string
*/
protected $redirectTo = '/home';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('signed')->only('verify');
$this->middleware('throttle:6,1')->only('verify', 'resend');
}
}

View File

@ -0,0 +1,100 @@
<?php
namespace App\Http\Controllers;
use App\Http\Requests\LoginRequest;
use App\Http\Requests\RegisterRequest;
use App\Repositories\UsersRepositoryInterface;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\ValidationException;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Validator, DB, Hash, Mail;
class UsersController extends Controller
{
private UsersRepositoryInterface $usersRepository;
public function __construct(UsersRepositoryInterface $usersRepository)
{
$this->usersRepository = $usersRepository;
}
public function register(RegisterRequest $request)
{
$email = $request->input('email');
$password = $request->input('password');
$this->usersRepository->create([
'email' => $email,
'password' => bcrypt($password),
]);
$token = auth()->attempt($request->only(['email', 'password']));
return response()->json([
'success' => true,
'data' => ['token' => $token]
]);
}
/**
* API Login, on success return JWT Auth token
*
* @param LoginRequest $request
* @return JsonResponse
*/
public function login(LoginRequest $request)
{
if (!$request->validated()) {
return response()->json(['success' => false, 'error' => $request->messages()], 401);
}
try {
if (!$token = auth()->attempt($request->only('email', 'password'))) {
return response()->json(['success' => false, 'error' => 'We cant find an account with this credentials. Please make sure you entered the right information and you have verified your email address.'], 404);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['success' => false, 'error' => 'Failed to login, please try again.'], 500);
}
// all good so return the token
return response()->json(['success' => true, 'data' => ['token' => $token]], 200);
}
/**
* Log out
* Invalidate the token, so user cannot use it anymore
* They have to login again to get a new token
*
* @param Request $request
* @return JsonResponse
* @throws ValidationException
*/
public function logout(Request $request)
{
$this->validate($request, ['token' => 'required']);
try {
auth()->invalidate($request->input('token'));
return response()->json(['success' => true, 'message' => "You have successfully logged out."]);
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['success' => false, 'error' => 'Failed to logout, please try again.'], 500);
}
}
public function me()
{
$requestedBy = auth()->user();
return response()->json([
'success' => true, 'data' => $requestedBy
]);
}
}

View File

@ -51,16 +51,20 @@ class Kernel extends HttpKernel
* @var array
*/
protected $routeMiddleware = [
'assign.guard.auth' => \App\Http\Middleware\AssignGuardAndAuthenticate::class,
'assign.guard' => \App\Http\Middleware\AssignGuardOnly::class,
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
];
/**

View File

@ -0,0 +1,29 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
class AssignGuardAndAuthenticate
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param null $guard
* @return mixed
* @throws AuthorizationException exception
*/
public function handle($request, Closure $next, $guard = null)
{
if($guard != null) {
auth()->shouldUse($guard);
if (!auth()->check())
return abort(401);
}
return $next($request);
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
class AssignGuardOnly
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
* @param null $guard
* @return mixed
* @throws AuthorizationException exception
*/
public function handle($request, Closure $next, $guard = null)
{
if($guard != null) {
auth()->shouldUse($guard);
}
return $next($request);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required',
];
}
public function messages()
{
return [
'email.required' => 'Email is required',
'password.required' => 'Password is required',
];
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class RegisterRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'email' => 'required|email',
'password' => 'required',
];
}
public function messages()
{
return [
'email.required' => 'Email is required',
'password.required' => 'Password is required',
];
}
}

46
app/Models/User.php Normal file
View File

@ -0,0 +1,46 @@
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
class User extends Authenticatable implements JWTSubject
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected array $fillable = [
'email', 'password', 'role'
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected array $hidden = [
'password', 'created_at', 'updated_at'
];
/**
* Get the identifier that will be stored in the subject claim of the JWT.
*
* @return mixed
*/
public function getJWTIdentifier()
{
return $this->getKey();
}
/**
* Return a key value array, containing any custom claims to be added to the JWT.
*
* @return array
*/
public function getJWTCustomClaims()
{
return [];
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace App\Policies;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class UserPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can view any models.
*
* @param \App\Models\User $user
* @return mixed
*/
public function viewAny(User $user)
{
return $user->type >= 128;
}
/**
* Determine whether the user can view the model.
*
* @param \App\Models\User $user
* @param \App\Models\User $model
* @return mixed
*/
public function view(User $user, User $model)
{
return $user->type >= 128;
}
/**
* Determine whether the user can create models.
*
* @param \App\Models\User $user
* @return mixed
*/
public function create(User $user)
{
return $user->type >= 128;
}
/**
* Determine whether the user can update the model.
*
* @param \App\Models\User $user
* @param \App\Models\User $model
* @return mixed
*/
public function update(User $user, User $model)
{
return $user->type >= 128;
}
/**
* Determine whether the user can delete the model.
*
* @param \App\Models\User $user
* @param \App\Models\User $model
* @return mixed
*/
public function delete(User $user, User $model)
{
return $user->type >= 128;
}
/**
* Determine whether the user can restore the model.
*
* @param \App\Models\User $user
* @param \App\Models\User $model
* @return mixed
*/
public function restore(User $user, User $model)
{
return $user->type >= 128;
}
/**
* Determine whether the user can permanently delete the model.
*
* @param \App\Models\User $user
* @param \App\Models\User $model
* @return mixed
*/
public function forceDelete(User $user, User $model)
{
return $user->type >= 128;
}
}

View File

@ -3,6 +3,7 @@
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Schema;
class AppServiceProvider extends ServiceProvider
{
@ -23,6 +24,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot()
{
//
Schema::defaultStringLength(191);
}
}

View File

@ -2,8 +2,9 @@
namespace App\Providers;
use App\Models\User;
use App\Policies\UserPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
class AuthServiceProvider extends ServiceProvider
{
@ -13,7 +14,8 @@ class AuthServiceProvider extends ServiceProvider
* @var array
*/
protected $policies = [
// 'App\Model' => 'App\Policies\ModelPolicy',
'App\Model' => 'App\Policies\ModelPolicy',
User::class => UserPolicy::class,
];
/**
@ -24,7 +26,5 @@ class AuthServiceProvider extends ServiceProvider
public function boot()
{
$this->registerPolicies();
//
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace App\Providers;
use App\Repositories\UsersRepository;
use App\Repositories\UsersRepositoryInterface;
use Illuminate\Support\ServiceProvider;
class RepositoriesServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap services.
*
* @return void
*/
public function boot()
{
$this->app->bind(UsersRepositoryInterface::class, UsersRepository::class);
}
}

View File

@ -25,6 +25,8 @@ class RouteServiceProvider extends ServiceProvider
{
//
Route::pattern('id', '[0-9]+');
parent::boot();
}

View File

@ -0,0 +1,42 @@
<?php
namespace App\Repositories;
use App\Models\User;
class UsersRepository implements UsersRepositoryInterface
{
public function getAll()
{
return User::all();
}
public function paginate($chunkSize)
{
return User::paginate($chunkSize);
}
public function create($data)
{
return User::create($data);
}
public function delete($id)
{
$user = User::find($id);
return $user->delete();
}
public function getById($id)
{
$user = User::find($id);
return $user;
}
public function getWhereEquals($field, $actual)
{
return User::where($field, $actual);
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace App\Repositories;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
interface UsersRepositoryInterface
{
/**
* Get all Users.
* @return User[]|Collection
*/
public function getAll();
/**
* Paginate Users, chunkSize is the size of one page.
* @param $chunkSize
* @return mixed
*/
public function paginate($chunkSize);
/**
* Create user with given data.
* @param $data
* @return mixed
*/
public function create($data);
/**
* Delete user with given id.
* @param $id
* @return mixed
*/
public function delete($id);
/**
* Get user by id.
* @param $userId
* @return mixed
*/
public function getById($userId);
/**
* Get the users where $field is equal to $actual.
* @param $field
* @param $actual
* @return mixed
*/
public function getWhereEquals($field, $actual);
}

View File

@ -0,0 +1,48 @@
<?php
namespace App\Traits;
use Illuminate\Database\Eloquent\Builder;
trait HasCompositeKey
{
/**
* Set the keys for a save update query.
*
* @param \Illuminate\Database\Eloquent\Builder $query
* @return \Illuminate\Database\Eloquent\Builder
*/
protected function setKeysForSaveQuery(Builder $query)
{
$keys = $this->getKeyName();
if (!is_array($keys)) {
return parent::setKeysForSaveQuery($query);
}
foreach ($keys as $keyName) {
$query->where($keyName, '=', $this->getKeyForSaveQuery($keyName));
}
return $query;
}
/**
* Get the primary key value for a save query.
*
* @param mixed $keyName
* @return mixed
*/
protected function getKeyForSaveQuery($keyName = null)
{
if (is_null($keyName)) {
$keyName = $this->getKeyName();
}
if (isset($this->original[$keyName])) {
return $this->original[$keyName];
}
return $this->getAttribute($keyName);
}
}

View File

@ -1,39 +0,0 @@
<?php
namespace App;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'name', 'email', 'password',
];
/**
* The attributes that should be hidden for arrays.
*
* @var array
*/
protected $hidden = [
'password', 'remember_token',
];
/**
* The attributes that should be cast to native types.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}

View File

@ -1,17 +1,20 @@
{
"name": "laravel/laravel",
"name": "jwtauth/laravel",
"type": "project",
"description": "The Laravel Framework.",
"description": "The JWT authenticate api in Laravel 6 template.",
"keywords": [
"framework",
"laravel"
],
"license": "MIT",
"require": {
"php": "^7.2",
"php": "^7.4",
"bensampo/laravel-enum": "^1.26",
"fideloper/proxy": "^4.0",
"laravel/framework": "^6.2",
"laravel/tinker": "^1.0"
"hesto/multi-auth": "^2.0",
"laravel/framework": "6.0",
"laravel/tinker": "^1.0",
"tymon/jwt-auth": "^1.0"
},
"require-dev": {
"facade/ignition": "^1.4",

1185
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -80,7 +80,7 @@ return [
|
*/
'locale' => 'en',
'locale' => 'pl',
/*
|--------------------------------------------------------------------------
@ -174,6 +174,12 @@ return [
// App\Providers\BroadcastServiceProvider::class,
App\Providers\EventServiceProvider::class,
App\Providers\RouteServiceProvider::class,
App\Providers\RepositoriesServiceProvider::class,
/*
* JWT-auth provider
*/
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],
@ -226,6 +232,9 @@ return [
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class,
'JWTFactory' => Tymon\JWTAuth\Facades\JWTFactory::class,
],
];

View File

@ -14,7 +14,7 @@ return [
*/
'defaults' => [
'guard' => 'web',
'guard' => 'users',
'passwords' => 'users',
],
@ -36,16 +36,10 @@ return [
*/
'guards' => [
'web' => [
'driver' => 'session',
'users' => [
'driver' => 'jwt',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
],
/*
@ -68,13 +62,8 @@ return [
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
'model' => App\Models\User::class,
],
// 'users' => [
// 'driver' => 'database',
// 'table' => 'users',
// ],
],
/*
@ -95,23 +84,9 @@ return [
'passwords' => [
'users' => [
'provider' => 'users',
'table' => 'password_resets',
'table' => 'user_password_resets',
'expire' => 60,
'throttle' => 60,
],
],
/*
|--------------------------------------------------------------------------
| Password Confirmation Timeout
|--------------------------------------------------------------------------
|
| Here you may define the amount of seconds before a password confirmation
| times out and the user is prompted to re-enter their password via the
| confirmation screen. By default, the timeout lasts for three hours.
|
*/
'password_timeout' => 10800,
];

View File

@ -44,7 +44,7 @@ return [
*/
'argon' => [
'memory' => 1024,
'memory' => 8192,
'threads' => 2,
'time' => 2,
],

304
config/jwt.php Normal file
View File

@ -0,0 +1,304 @@
<?php
/*
* This file is part of jwt-auth.
*
* (c) Sean Tymon <tymon148@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
return [
/*
|--------------------------------------------------------------------------
| JWT Authentication Secret
|--------------------------------------------------------------------------
|
| Don't forget to set this in your .env file, as it will be used to sign
| your tokens. A helper command is provided for this:
| `php artisan jwt:secret`
|
| Note: This will be used for Symmetric algorithms only (HMAC),
| since RSA and ECDSA use a private/public key combo (See below).
|
*/
'secret' => env('JWT_SECRET'),
/*
|--------------------------------------------------------------------------
| JWT Authentication Keys
|--------------------------------------------------------------------------
|
| The algorithm you are using, will determine whether your tokens are
| signed with a random string (defined in `JWT_SECRET`) or using the
| following public & private keys.
|
| Symmetric Algorithms:
| HS256, HS384 & HS512 will use `JWT_SECRET`.
|
| Asymmetric Algorithms:
| RS256, RS384 & RS512 / ES256, ES384 & ES512 will use the keys below.
|
*/
'keys' => [
/*
|--------------------------------------------------------------------------
| Public Key
|--------------------------------------------------------------------------
|
| A path or resource to your public key.
|
| E.g. 'file://path/to/public/key'
|
*/
'public' => env('JWT_PUBLIC_KEY'),
/*
|--------------------------------------------------------------------------
| Private Key
|--------------------------------------------------------------------------
|
| A path or resource to your private key.
|
| E.g. 'file://path/to/private/key'
|
*/
'private' => env('JWT_PRIVATE_KEY'),
/*
|--------------------------------------------------------------------------
| Passphrase
|--------------------------------------------------------------------------
|
| The passphrase for your private key. Can be null if none set.
|
*/
'passphrase' => env('JWT_PASSPHRASE'),
],
/*
|--------------------------------------------------------------------------
| JWT time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token will be valid for.
| Defaults to 1 hour.
|
| You can also set this to null, to yield a never expiring token.
| Some people may want this behaviour for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
| Notice: If you set this to null you should remove 'exp' element from 'required_claims' list.
|
*/
'ttl' => env('JWT_TTL', 60),
/*
|--------------------------------------------------------------------------
| Refresh time to live
|--------------------------------------------------------------------------
|
| Specify the length of time (in minutes) that the token can be refreshed
| within. I.E. The user can refresh their token within a 2 week window of
| the original token being created until they must re-authenticate.
| Defaults to 2 weeks.
|
| You can also set this to null, to yield an infinite refresh time.
| Some may want this instead of never expiring tokens for e.g. a mobile app.
| This is not particularly recommended, so make sure you have appropriate
| systems in place to revoke the token if necessary.
|
*/
'refresh_ttl' => env('JWT_REFRESH_TTL', 20160),
/*
|--------------------------------------------------------------------------
| JWT hashing algorithm
|--------------------------------------------------------------------------
|
| Specify the hashing algorithm that will be used to sign the token.
|
| See here: https://github.com/namshi/jose/tree/master/src/Namshi/JOSE/Signer/OpenSSL
| for possible values.
|
*/
'algo' => env('JWT_ALGO', 'HS256'),
/*
|--------------------------------------------------------------------------
| Required Claims
|--------------------------------------------------------------------------
|
| Specify the required claims that must exist in any token.
| A TokenInvalidException will be thrown if any of these claims are not
| present in the payload.
|
*/
'required_claims' => [
'iss',
'iat',
'exp',
'nbf',
'sub',
'jti',
],
/*
|--------------------------------------------------------------------------
| Persistent Claims
|--------------------------------------------------------------------------
|
| Specify the claim keys to be persisted when refreshing a token.
| `sub` and `iat` will automatically be persisted, in
| addition to the these claims.
|
| Note: If a claim does not exist then it will be ignored.
|
*/
'persistent_claims' => [
// 'foo',
// 'bar',
],
/*
|--------------------------------------------------------------------------
| Lock Subject
|--------------------------------------------------------------------------
|
| This will determine whether a `prv` claim is automatically added to
| the token. The purpose of this is to ensure that if you have multiple
| authentication models e.g. `App\User` & `App\OtherPerson`, then we
| should prevent one authentication request from impersonating another,
| if 2 tokens happen to have the same id across the 2 different models.
|
| Under specific circumstances, you may want to disable this behaviour
| e.g. if you only have one authentication model, then you would save
| a little on token size.
|
*/
'lock_subject' => true,
/*
|--------------------------------------------------------------------------
| Leeway
|--------------------------------------------------------------------------
|
| This property gives the jwt timestamp claims some "leeway".
| Meaning that if you have any unavoidable slight clock skew on
| any of your servers then this will afford you some level of cushioning.
|
| This applies to the claims `iat`, `nbf` and `exp`.
|
| Specify in seconds - only if you know you need it.
|
*/
'leeway' => env('JWT_LEEWAY', 0),
/*
|--------------------------------------------------------------------------
| Blacklist Enabled
|--------------------------------------------------------------------------
|
| In order to invalidate tokens, you must have the blacklist enabled.
| If you do not want or need this functionality, then set this to false.
|
*/
'blacklist_enabled' => env('JWT_BLACKLIST_ENABLED', true),
/*
| -------------------------------------------------------------------------
| Blacklist Grace Period
| -------------------------------------------------------------------------
|
| When multiple concurrent requests are made with the same JWT,
| it is possible that some of them fail, due to token regeneration
| on every request.
|
| Set grace period in seconds to prevent parallel request failure.
|
*/
'blacklist_grace_period' => env('JWT_BLACKLIST_GRACE_PERIOD', 0),
/*
|--------------------------------------------------------------------------
| Cookies encryption
|--------------------------------------------------------------------------
|
| By default Laravel encrypt cookies for security reason.
| If you decide to not decrypt cookies, you will have to configure Laravel
| to not encrypt your cookie token by adding its name into the $except
| array available in the middleware "EncryptCookies" provided by Laravel.
| see https://laravel.com/docs/master/responses#cookies-and-encryption
| for details.
|
| Set it to true if you want to decrypt cookies.
|
*/
'decrypt_cookies' => false,
/*
|--------------------------------------------------------------------------
| Providers
|--------------------------------------------------------------------------
|
| Specify the various providers used throughout the package.
|
*/
'providers' => [
/*
|--------------------------------------------------------------------------
| JWT Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to create and decode the tokens.
|
*/
'jwt' => Tymon\JWTAuth\Providers\JWT\Lcobucci::class,
/*
|--------------------------------------------------------------------------
| Authentication Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to authenticate users.
|
*/
'auth' => Tymon\JWTAuth\Providers\Auth\Illuminate::class,
/*
|--------------------------------------------------------------------------
| Storage Provider
|--------------------------------------------------------------------------
|
| Specify the provider that is used to store tokens in the blacklist.
|
*/
'storage' => Tymon\JWTAuth\Providers\Storage\Illuminate::class,
],
];

View File

@ -1,6 +1,5 @@
<?php
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
@ -90,11 +89,6 @@ return [
'driver' => 'errorlog',
'level' => 'debug',
],
'null' => [
'driver' => 'monolog',
'handler' => NullHandler::class,
],
],
];

View File

@ -1,7 +1,7 @@
<?php
/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\User;
use App\Models\User;
use Faker\Generator as Faker;
use Illuminate\Support\Str;

View File

@ -1,32 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePasswordResetsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('password_resets', function (Blueprint $table) {
$table->string('email')->index();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('password_resets');
}
}

View File

@ -1,35 +0,0 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateFailedJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('failed_jobs', function (Blueprint $table) {
$table->bigIncrements('id');
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('failed_jobs');
}
}

View File

@ -1,8 +1,9 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use App\Enums\UserRole;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateUsersTable extends Migration
{
@ -15,11 +16,9 @@ class CreateUsersTable extends Migration
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->integer('role')->default(UserRole::USER);
$table->timestamps();
});
}

View File

@ -11,6 +11,6 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
// $this->call(UsersTableSeeder::class);
$this->call(UsersTableSeeder::class);
}
}

View File

@ -0,0 +1,22 @@
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
DB::table('admin')->insert([
'name' => 'adminTargi',
'email' => 'adminTargi@poleng.pl',
'password' => bcrypt('password'),
'type' => 256
]);
}
}

View File

@ -1,21 +0,0 @@
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "npm run development -- --watch",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.19",
"cross-env": "^5.1",
"laravel-mix": "^4.0.7",
"lodash": "^4.17.13",
"resolve-url-loader": "^2.3.1",
"sass": "^1.15.2",
"sass-loader": "^7.1.0"
}
}

View File

@ -17,6 +17,5 @@ return [
'sent' => 'We have e-mailed your password reset link!',
'token' => 'This password reset token is invalid.',
'user' => "We can't find a user with that e-mail address.",
'throttled' => 'Please wait before retrying.',
];

View File

@ -93,7 +93,6 @@ return [
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'password' => 'The password is incorrect.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
'required' => 'The :attribute field is required.',

View File

@ -1,100 +0,0 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Nunito', sans-serif;
font-weight: 200;
height: 100vh;
margin: 0;
}
.full-height {
height: 100vh;
}
.flex-center {
align-items: center;
display: flex;
justify-content: center;
}
.position-ref {
position: relative;
}
.top-right {
position: absolute;
right: 10px;
top: 18px;
}
.content {
text-align: center;
}
.title {
font-size: 84px;
}
.links > a {
color: #636b6f;
padding: 0 25px;
font-size: 13px;
font-weight: 600;
letter-spacing: .1rem;
text-decoration: none;
text-transform: uppercase;
}
.m-b-md {
margin-bottom: 30px;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
@if (Route::has('login'))
<div class="top-right links">
@auth
<a href="{{ url('/home') }}">Home</a>
@else
<a href="{{ route('login') }}">Login</a>
@if (Route::has('register'))
<a href="{{ route('register') }}">Register</a>
@endif
@endauth
</div>
@endif
<div class="content">
<div class="title m-b-md">
Laravel
</div>
<div class="links">
<a href="https://laravel.com/docs">Docs</a>
<a href="https://laracasts.com">Laracasts</a>
<a href="https://laravel-news.com">News</a>
<a href="https://blog.laravel.com">Blog</a>
<a href="https://nova.laravel.com">Nova</a>
<a href="https://forge.laravel.com">Forge</a>
<a href="https://vapor.laravel.com">Vapor</a>
<a href="https://github.com/laravel/laravel">GitHub</a>
</div>
</div>
</div>
</body>
</html>

View File

@ -1,18 +1,9 @@
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
Route::group(['prefix' => 'user', 'middleware' => ['assign.guard:users']], function () {
Route::post('/register', 'UsersController@register');
Route::post('/login', 'UsersController@login');
Route::get('/me', 'UsersController@me');
});

View File

@ -10,7 +10,3 @@
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});

View File

@ -1,21 +0,0 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* @return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace Tests\Feature;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class UsersTest extends TestCase
{
use RefreshDatabase;
private array $loginData = ['email' => 'artur.nowakowski@gmail.com', 'password' => 'test123'];
public function getToken()
{
$response = $this->post('http://localhost:8000/api/user/login', $this->loginData)->decodeResponseJson();
return $response['data']['token'];
}
/**
* A basic test example.
*
* @return void
*/
public function testRegistration()
{
$response = $this->post('http://localhost:8000/api/user/register', $this->loginData);
$response->assertStatus(200);
$response->assertJsonStructure(['success', 'data' => ['token']]);
}
public function testLogin()
{
$this->testRegistration();
$response = $this->post('http://localhost:8000/api/user/login', $this->loginData);
$response->assertStatus(200);
$response->assertJsonStructure(['success', 'data' => ['token']]);
}
public function testMe()
{
$this->testRegistration();
$token = $this->getToken();
$response = $this->get('http://localhost:8000/api/user/me', ['Authorization' => "Bearer $token"]);
$response->assertStatus(200);
$response->assertJsonStructure(['success', 'data' => ['email', 'role']]);
}
}

View File

@ -1,15 +0,0 @@
const mix = require('laravel-mix');
/*
|--------------------------------------------------------------------------
| Mix Asset Management
|--------------------------------------------------------------------------
|
| Mix provides a clean, fluent API for defining some Webpack build steps
| for your Laravel application. By default, we are compiling the Sass
| file for the application as well as bundling up all the JS files.
|
*/
mix.js('resources/js/app.js', 'public/js')
.sass('resources/sass/app.scss', 'public/css');