User od tego, kto naprawdę z niego korzysta.
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
interface UserRegistrar
{
public UserRegistrar(DatabaseContext database, Hasher hasher, Mailer mailer)
public User register(string email, string password, bool agreementsApproved)
}
Request czy jakieś ogólne luźne struktury danych. Klasa musi być zdatna do uruchomienia wszędzie i w każdy sposób.
class UserRegistrar implements UserRegistrarContract
{
public User register(string email, string password, bool agreementsApproved)
{
if (!agreementsApproved) {
throw new AgreementsNotApprovedException()
}
if (this.database.select(User.class).where("email", email).count() > 0) {
throw new UserAlreadyExistsException()
}
User user = new User
user.email = email
user.password = this.hasher.hash(password).toString()
this.database.commit(user)
this.mailer.send(new UserRegistered(user))
return user
}
}
UNIQUE na kolumny z emailem, jeżeli chcemy zachować jego unikalnośćUserAlreadyExistsException lepiej złapać w kontrolerze i wyświetlić ogólny komunikat zamiast błędu, który wskazywałby, że użytkownik faktycznie u nas już jest zarejestrowanyinterface UserAuthenticator
{
public UserAuthenticator(DatabaseContext database, SessionContext session, Hasher hasher)
public string authenticate(User user, string password)
}
class UserAuthenticator implements UserAuthenticatorContract
{
public string authenticate(User user, string password)
{
?User user = this.database.select(User.class).where("email", email).first()
if (user === null) {
this.hasher.hash(rand())
throw new UserNotFoundException()
}
bool result = this.hasher.verify(password, user.password)
if (!result) {
throw new WrongPasswordException()
}
return this.session.registerUser(user).getSessionId()
}
}
POST!), ale niektórzy cyklicznie usuwają sesje, jeżeli użytkownik dawno z niej nie korzystałinterface UserAuthenticator
{
public UserAuthenticator(DatabaseContext database, JwtService jwt, Hasher hasher)
public string authenticate(User user, string password)
}
class UserAuthenticator implements UserAuthenticatorContract
{
public string authenticate(User user, string password)
{
?User user = this.database.select(User.class).where("email", email).first()
if (user === null) {
this.hasher.hash(rand())
throw new UserNotFoundException()
}
bool result = this.hasher.verify(password, user.password)
if (!result) {
throw new WrongPasswordException()
}
return this.jwt.generateToken(user)
}
}
Authentication jako beareris_admin z wartościami zero lub jeden.
class OnlyAdminMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if not request.user.is_admin:
raise ResourceForbiddenException()
return self.get_response(request)
enum UserRole: int
{
case Administrator = 1;
case Moderator = 2;
case Reviewer = 4;
case Participant = 8;
}
# (...)
return (bool)($user->getRoleIndicator() & UserRole::Administrator);
| aktor | moduł | akcja | dostęp |
|---|---|---|---|
| user:1 | users | get | true |
| user:1 | users | post | false |
| user:1 | user | get | true |
| user:1 | user | patch | false |
| user:1 | user | delete | false |
false - jeżeli nie znajdą się w bazie danych, oznacza to, że użytkownik nie ma prawa dostępuSELECT COUNT(id) FROM permissions WHERE user_id = ? AND module = ? AND action = ? wystarczy do sprawdzenia uprawnienia