@@ -0,0 +1,41 @@
|
||||
package repository
|
||||
|
||||
// Tokens represents a user record with info and permissions.
|
||||
type Tokens struct {
|
||||
ID int64 `json:"id"`
|
||||
Name string `json:"name"`
|
||||
LastName string `json:"last_name"`
|
||||
Login string `json:"login"`
|
||||
Token string `json:"token"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
}
|
||||
|
||||
// TokenCreate is the request body for creating a new user.
|
||||
type TokenCreate struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
LastName string `json:"last_name" binding:"required"`
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
}
|
||||
|
||||
// LoginRequest is the request body for login.
|
||||
type LoginRequest struct {
|
||||
Login string `json:"login" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
}
|
||||
|
||||
// LoginResponse is returned after successful login.
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
Name string `json:"name"`
|
||||
LastName string `json:"last_name"`
|
||||
Login string `json:"login"`
|
||||
PermissionView bool `json:"permission_view"`
|
||||
PermissionManage bool `json:"permission_manage_agent"`
|
||||
PermissionAdmin bool `json:"permission_admin"`
|
||||
}
|
||||
@@ -1,11 +1,187 @@
|
||||
package repository
|
||||
|
||||
import "database/sql"
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/storage"
|
||||
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/utils"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// Repository wraps a SQLite database connection.
|
||||
type Repository struct {
|
||||
DB *sql.DB
|
||||
}
|
||||
|
||||
// New creates a new Repository.
|
||||
func New(db *sql.DB) *Repository {
|
||||
return &Repository{DB: db}
|
||||
}
|
||||
|
||||
var ErrNotFound = errors.New("not found")
|
||||
|
||||
// Init creates the tokens table if it does not exist.
|
||||
func (r *Repository) Init() error {
|
||||
_, err := r.DB.Exec(storage.CreateSqlite)
|
||||
return err
|
||||
}
|
||||
|
||||
// CreateToken inserts a new user record with hashed password and generated token.
|
||||
func (r *Repository) CreateToken(tc TokenCreate) (string, error) {
|
||||
hashed, err := bcrypt.GenerateFromPassword([]byte(tc.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
token, err := utils.RandomToken()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
result, err := r.DB.Exec(
|
||||
`INSERT INTO tokens (name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||
tc.Name, tc.LastName, tc.Login, string(hashed), token,
|
||||
tc.PermissionView, tc.PermissionManage, tc.PermissionAdmin,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
id, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strconv.FormatInt(id, 10), nil
|
||||
}
|
||||
|
||||
// Login authenticates by login/password, generates a new token, and returns LoginResponse.
|
||||
func (r *Repository) Login(login, password string) (*LoginResponse, error) {
|
||||
var t Tokens
|
||||
var hashedPassword string
|
||||
|
||||
err := r.DB.QueryRow(
|
||||
`SELECT id, name, last_name, login, password, token, permission_view, permission_manage_agent, permission_admin
|
||||
FROM tokens WHERE login = ?`,
|
||||
login,
|
||||
).Scan(&t.ID, &t.Name, &t.LastName, &t.Login, &hashedPassword, &t.Token,
|
||||
&t.PermissionView, &t.PermissionManage, &t.PermissionAdmin)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password)); err != nil {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
// Generate new token on each login
|
||||
newToken, err := utils.RandomToken()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, err = r.DB.Exec(`UPDATE tokens SET token = ? WHERE id = ?`, newToken, t.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LoginResponse{
|
||||
Token: newToken,
|
||||
Name: t.Name,
|
||||
LastName: t.LastName,
|
||||
Login: t.Login,
|
||||
PermissionView: t.PermissionView,
|
||||
PermissionManage: t.PermissionManage,
|
||||
PermissionAdmin: t.PermissionAdmin,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetTokenByToken retrieves a user record by token value.
|
||||
func (r *Repository) GetToken(token string) (*Tokens, error) {
|
||||
var t Tokens
|
||||
err := r.DB.QueryRow(
|
||||
`SELECT id, name, last_name, login, token, permission_view, permission_manage_agent, permission_admin
|
||||
FROM tokens WHERE token = ?`,
|
||||
token,
|
||||
).Scan(&t.ID, &t.Name, &t.LastName, &t.Login, &t.Token,
|
||||
&t.PermissionView, &t.PermissionManage, &t.PermissionAdmin)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
return &t, nil
|
||||
}
|
||||
|
||||
// ListTokens returns all users without password and token.
|
||||
func (r *Repository) ListTokens() ([]Tokens, error) {
|
||||
rows, err := r.DB.Query(
|
||||
`SELECT id, name, last_name, login, permission_view, permission_manage_agent, permission_admin
|
||||
FROM tokens`,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var tokens []Tokens
|
||||
for rows.Next() {
|
||||
var t Tokens
|
||||
if err := rows.Scan(&t.ID, &t.Name, &t.LastName, &t.Login,
|
||||
&t.PermissionView, &t.PermissionManage, &t.PermissionAdmin); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tokens = append(tokens, t)
|
||||
}
|
||||
return tokens, rows.Err()
|
||||
}
|
||||
|
||||
// DeleteToken deletes a user by token value.
|
||||
func (r *Repository) DeleteToken(token string) error {
|
||||
result, err := r.DB.Exec(`DELETE FROM tokens WHERE token = ?`, token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteTokenByLogin deletes a user by login.
|
||||
func (r *Repository) DeleteTokenByLogin(login string) error {
|
||||
result, err := r.DB.Exec(`DELETE FROM tokens WHERE login = ?`, login)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
affected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if affected == 0 {
|
||||
return ErrNotFound
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExistsByLogin checks if a user with given login exists.
|
||||
func (r *Repository) ExistsByLogin(login string) bool {
|
||||
var count int
|
||||
err := r.DB.QueryRow(`SELECT COUNT(*) FROM tokens WHERE login = ?`, login).Scan(&count)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return count > 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user