add ban by login attempts and fix sql
This commit is contained in:
parent
a27c8669fc
commit
00b8636b5a
@ -122,9 +122,10 @@ func (a *App) Run(p RunParams) {
|
|||||||
shortlinkRepo = repos.NewShortlinkRepo(sqlDb, tracer)
|
shortlinkRepo = repos.NewShortlinkRepo(sqlDb, tracer)
|
||||||
eventRepo = repos.NewEventRepo(kafka)
|
eventRepo = repos.NewEventRepo(kafka)
|
||||||
|
|
||||||
userCache = cache.NewCacheInmemSharded[models.UserDTO](cache.ShardingTypeInteger)
|
userCache = cache.NewCacheInmemSharded[models.UserDTO](cache.ShardingTypeInteger)
|
||||||
jwtCache = cache.NewCacheInmemSharded[string](cache.ShardingTypeJWT)
|
loginAttemptsCache = cache.NewCacheInmem[string, int]()
|
||||||
linksCache = cache.NewCacheInmem[string, string]()
|
jwtCache = cache.NewCacheInmemSharded[string](cache.ShardingTypeJWT)
|
||||||
|
linksCache = cache.NewCacheInmem[string, string]()
|
||||||
)
|
)
|
||||||
|
|
||||||
// Periodically trigger cache cleanup
|
// Periodically trigger cache cleanup
|
||||||
@ -140,20 +141,22 @@ func (a *App) Run(p RunParams) {
|
|||||||
userCache.CheckExpired()
|
userCache.CheckExpired()
|
||||||
jwtCache.CheckExpired()
|
jwtCache.CheckExpired()
|
||||||
linksCache.CheckExpired()
|
linksCache.CheckExpired()
|
||||||
|
loginAttemptsCache.CheckExpired()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
userService = services.NewUserService(
|
userService = services.NewUserService(
|
||||||
services.UserServiceDeps{
|
services.UserServiceDeps{
|
||||||
Jwt: jwtUtil,
|
Jwt: jwtUtil,
|
||||||
Password: passwordUtil,
|
Password: passwordUtil,
|
||||||
UserRepo: userRepo,
|
UserRepo: userRepo,
|
||||||
UserCache: userCache,
|
UserCache: userCache,
|
||||||
JwtCache: jwtCache,
|
LoginAttemptsCache: loginAttemptsCache,
|
||||||
EventRepo: *eventRepo,
|
JwtCache: jwtCache,
|
||||||
ActionTokenRepo: actionTokenRepo,
|
EventRepo: *eventRepo,
|
||||||
Logger: logger,
|
ActionTokenRepo: actionTokenRepo,
|
||||||
|
Logger: logger,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
shortlinkService = services.NewShortlinkSevice(
|
shortlinkService = services.NewShortlinkSevice(
|
||||||
|
|||||||
@ -26,7 +26,7 @@ type actionTokenRepo struct {
|
|||||||
func (a *actionTokenRepo) CreateActionToken(ctx context.Context, dto models.ActionTokenDTO) (*models.ActionTokenDTO, error) {
|
func (a *actionTokenRepo) CreateActionToken(ctx context.Context, dto models.ActionTokenDTO) (*models.ActionTokenDTO, error) {
|
||||||
query := `
|
query := `
|
||||||
insert into
|
insert into
|
||||||
action_tokens (user_id, value, target, expiration)
|
action_tokens (user_id, value, target, expires_at)
|
||||||
values ($1, $2, $3, $4)
|
values ($1, $2, $3, $4)
|
||||||
returning id;`
|
returning id;`
|
||||||
row := a.db.QueryRowContext(ctx, query, dto.UserId, dto.Value, dto.Target, dto.Expiration)
|
row := a.db.QueryRowContext(ctx, query, dto.UserId, dto.Value, dto.Target, dto.Expiration)
|
||||||
@ -51,7 +51,7 @@ func (a *actionTokenRepo) GetActionToken(ctx context.Context, value string, targ
|
|||||||
select id, user_id from action_tokens
|
select id, user_id from action_tokens
|
||||||
where
|
where
|
||||||
value=$1 and target=$2
|
value=$1 and target=$2
|
||||||
and CURRENT_TIMESTAMP < expiration;`
|
and CURRENT_TIMESTAMP < expires_at;`
|
||||||
row := a.db.QueryRowContext(ctx, query, value, target)
|
row := a.db.QueryRowContext(ctx, query, value, target)
|
||||||
|
|
||||||
err := row.Scan(&dto.Id, &dto.UserId)
|
err := row.Scan(&dto.Id, &dto.UserId)
|
||||||
|
|||||||
@ -93,7 +93,7 @@ func (u *userRepo) GetUserById(ctx context.Context, id string) (*models.UserDTO,
|
|||||||
|
|
||||||
query := `
|
query := `
|
||||||
select id, email, secret, full_name, email_verified
|
select id, email, secret, full_name, email_verified
|
||||||
from users where id = $1 and activated;`
|
from users where id = $1 and active;`
|
||||||
row := u.db.QueryRowContext(ctx, query, id)
|
row := u.db.QueryRowContext(ctx, query, id)
|
||||||
|
|
||||||
dto := &models.UserDTO{}
|
dto := &models.UserDTO{}
|
||||||
@ -113,7 +113,7 @@ func (u *userRepo) GetUserByEmail(ctx context.Context, login string) (*models.Us
|
|||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
query := `select id, email, secret, full_name, email_verified
|
query := `select id, email, secret, full_name, email_verified
|
||||||
from users where email = $1 and activated;`
|
from users where email = $1 and active;`
|
||||||
row := u.db.QueryRowContext(ctx, query, login)
|
row := u.db.QueryRowContext(ctx, query, login)
|
||||||
|
|
||||||
dto := &models.UserDTO{}
|
dto := &models.UserDTO{}
|
||||||
|
|||||||
@ -49,14 +49,15 @@ func NewUserService(deps UserServiceDeps) UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type UserServiceDeps struct {
|
type UserServiceDeps struct {
|
||||||
Jwt utils.JwtUtil
|
Jwt utils.JwtUtil
|
||||||
Password utils.PasswordUtil
|
Password utils.PasswordUtil
|
||||||
UserRepo repos.UserRepo
|
UserRepo repos.UserRepo
|
||||||
UserCache cache.Cache[string, models.UserDTO]
|
UserCache cache.Cache[string, models.UserDTO]
|
||||||
JwtCache cache.Cache[string, string]
|
JwtCache cache.Cache[string, string]
|
||||||
EventRepo repos.EventRepo
|
LoginAttemptsCache cache.Cache[string, int]
|
||||||
ActionTokenRepo repos.ActionTokenRepo
|
EventRepo repos.EventRepo
|
||||||
Logger logger.Logger
|
ActionTokenRepo repos.ActionTokenRepo
|
||||||
|
Logger logger.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
type userService struct {
|
type userService struct {
|
||||||
@ -108,6 +109,22 @@ func (u *userService) CreateUser(ctx context.Context, params UserCreateParams) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *userService) AuthenticateUser(ctx context.Context, email, password string) (string, error) {
|
func (u *userService) AuthenticateUser(ctx context.Context, email, password string) (string, error) {
|
||||||
|
attempts, ok := u.deps.LoginAttemptsCache.Get(email)
|
||||||
|
if ok && attempts >= 4 {
|
||||||
|
return "", fmt.Errorf("too many bad login attempts")
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := u.authenticateUser(ctx, email, password)
|
||||||
|
if err != nil {
|
||||||
|
u.deps.LoginAttemptsCache.Set(email, attempts+1, cache.Expiration{Ttl: 30 * time.Second})
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
u.deps.LoginAttemptsCache.Del(email)
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *userService) authenticateUser(ctx context.Context, email, password string) (string, error) {
|
||||||
user, err := u.deps.UserRepo.GetUserByEmail(ctx, email)
|
user, err := u.deps.UserRepo.GetUserByEmail(ctx, email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
|||||||
@ -4,11 +4,13 @@ create table if not exists users (
|
|||||||
secret varchar(256) not null,
|
secret varchar(256) not null,
|
||||||
full_name varchar(256) not null,
|
full_name varchar(256) not null,
|
||||||
email_verified boolean not null default false,
|
email_verified boolean not null default false,
|
||||||
active boolean,
|
active boolean default true,
|
||||||
created_at timestamp,
|
created_at timestamp,
|
||||||
updated_at timestamp
|
updated_at timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
|
alter table users alter column active set default true;
|
||||||
|
|
||||||
create index if not exists idx_users_email on users(email);
|
create index if not exists idx_users_email on users(email);
|
||||||
|
|
||||||
create or replace trigger trg_user_created
|
create or replace trigger trg_user_created
|
||||||
|
|||||||
@ -4,14 +4,15 @@ create table if not exists shortlinks (
|
|||||||
expires_at timestamp not null,
|
expires_at timestamp not null,
|
||||||
created_at timestamp,
|
created_at timestamp,
|
||||||
updated_at timestamp
|
updated_at timestamp
|
||||||
|
);
|
||||||
|
|
||||||
create or replace trigger trg_shortlink_created
|
create or replace trigger trg_shortlink_created
|
||||||
before insert on shortlinks
|
before insert on shortlinks
|
||||||
for each row
|
for each row
|
||||||
when new is distinct from old
|
|
||||||
execute function trg_proc_row_created();
|
execute function trg_proc_row_created();
|
||||||
|
|
||||||
create or replace trigger trg_shortlink_updated
|
create or replace trigger trg_shortlink_updated
|
||||||
before update on shortlinks
|
before update on shortlinks
|
||||||
for each row when new is distinct from old
|
for each row
|
||||||
|
when (new is distinct from old)
|
||||||
execute function trg_proc_row_updated();
|
execute function trg_proc_row_updated();
|
||||||
@ -1,26 +1,25 @@
|
|||||||
create table if not exists action_tokens (
|
create table if not exists action_tokens (
|
||||||
id int primary key generated always as identity,
|
id int generated always as identity,
|
||||||
user_id int references users(id),
|
user_id int references users(id),
|
||||||
value text not null,
|
value text not null,
|
||||||
target text not null,
|
target text not null,
|
||||||
expires_at timestamp not null,
|
expires_at timestamp not null,
|
||||||
created_at timestamp,
|
created_at timestamp,
|
||||||
updated_at timestamp
|
updated_at timestamp,
|
||||||
|
|
||||||
constraint pk_action_tokens_id primary key(id),
|
constraint pk_action_tokens_id primary key(id),
|
||||||
constraint check chk_action_tokens_target target in ('verify', 'restore')
|
constraint chk_action_tokens_target check(target in ('verify', 'restore'))
|
||||||
);
|
);
|
||||||
|
|
||||||
create index if not exists idx_action_tokens_value on actions_tokens(value);
|
create index if not exists idx_action_tokens_value on action_tokens(value);
|
||||||
|
|
||||||
create or replace trigger trg_action_token_created
|
create or replace trigger trg_action_token_created
|
||||||
before insert on action_tokens
|
before insert on action_tokens
|
||||||
for each row
|
for each row
|
||||||
when new is distinct from old
|
|
||||||
execute function trg_proc_row_created();
|
execute function trg_proc_row_created();
|
||||||
|
|
||||||
create or replace trigger trg_action_token_updated
|
create or replace trigger trg_action_token_updated
|
||||||
before update on action_tokens
|
before update on action_tokens
|
||||||
for each row
|
for each row
|
||||||
when new is distinct from old
|
when (new is distinct from old)
|
||||||
execute function trg_proc_row_updated();
|
execute function trg_proc_row_updated();
|
||||||
Loading…
x
Reference in New Issue
Block a user