+44
-3
@@ -1,4 +1,4 @@
|
|||||||
package cmd
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@@ -27,17 +27,37 @@ func main() {
|
|||||||
}
|
}
|
||||||
cfg, err := config.ImportSettings(cfg_path)
|
cfg, err := config.ImportSettings(cfg_path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Err loading config")
|
log.Fatalf("Err loading config: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db, err := storage.Open(cfg.Database.Token_db)
|
db, err := storage.Open(cfg.Database.Token_db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Err opening database")
|
log.Fatalf("Err opening database: %v", err)
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
h := handlers.New(db)
|
h := handlers.New(db)
|
||||||
agents := handlers.AgentsGroup{Handlers: h}
|
agents := handlers.AgentsGroup{Handlers: h}
|
||||||
|
auth := handlers.AuthGroup{Handlers: h}
|
||||||
|
|
||||||
|
// Create admin user from config if not exists
|
||||||
|
if cfg.Admin.Admin_login != "" && cfg.Admin.Admin_password != "" {
|
||||||
|
if !h.Repo.ExistsByLogin(cfg.Admin.Admin_login) {
|
||||||
|
_, err := h.Repo.CreateToken(repository.TokenCreate{
|
||||||
|
Name: cfg.Admin.Admin_name,
|
||||||
|
LastName: cfg.Admin.Admin_last_name,
|
||||||
|
Login: cfg.Admin.Admin_login,
|
||||||
|
Password: cfg.Admin.Admin_password,
|
||||||
|
PermissionView: true,
|
||||||
|
PermissionAdmin: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Warning: failed to create admin user: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Println("Admin user created from config")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
router := gin.Default()
|
router := gin.Default()
|
||||||
docs.SwaggerInfo.BasePath = "/api/v1"
|
docs.SwaggerInfo.BasePath = "/api/v1"
|
||||||
@@ -49,12 +69,33 @@ func main() {
|
|||||||
|
|
||||||
v1 := router.Group("/api/v1")
|
v1 := router.Group("/api/v1")
|
||||||
{
|
{
|
||||||
|
// Auth routes (public)
|
||||||
|
authGroup := v1.Group("/auth")
|
||||||
|
{
|
||||||
|
authGroup.POST("/login", auth.Login)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auth token management (requires auth)
|
||||||
|
authTokenGroup := v1.Group("/auth")
|
||||||
|
authTokenGroup.Use(auth.AuthMiddleware())
|
||||||
|
{
|
||||||
|
authTokenGroup.POST("/token", handlers.RequireAdmin(), auth.CreateToken)
|
||||||
|
authTokenGroup.GET("/validate", auth.ValidateToken)
|
||||||
|
authTokenGroup.GET("/tokens", handlers.RequireAdmin(), auth.ListTokens)
|
||||||
|
authTokenGroup.DELETE("/token", auth.DeleteMyToken)
|
||||||
|
authTokenGroup.DELETE("/tokens/:login", handlers.RequireAdmin(), auth.DeleteToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Agents (requires manage_agent permission)
|
||||||
agentsGroup := v1.Group("/agents")
|
agentsGroup := v1.Group("/agents")
|
||||||
|
agentsGroup.Use(auth.AuthMiddleware(), handlers.RequireManageAgent())
|
||||||
{
|
{
|
||||||
agentsGroup.GET("", agents.List)
|
agentsGroup.GET("", agents.List)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs (requires view permission)
|
||||||
logsGroup := v1.Group("/logs")
|
logsGroup := v1.Group("/logs")
|
||||||
|
logsGroup.Use(auth.AuthMiddleware(), handlers.RequireView())
|
||||||
{
|
{
|
||||||
if cfg.Database.Clickhouse_host != "" {
|
if cfg.Database.Clickhouse_host != "" {
|
||||||
chConn, err := storage.OpenClickHouse(storage.ClickHouseConfig{
|
chConn, err := storage.OpenClickHouse(storage.ClickHouseConfig{
|
||||||
|
|||||||
@@ -0,0 +1,182 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AuthGroup handles authentication routes.
|
||||||
|
type AuthGroup struct {
|
||||||
|
*Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
// Login authenticates a user by login and password, returns a token.
|
||||||
|
// @Summary Login
|
||||||
|
// @Description Authenticate with login and password, returns a token and permissions
|
||||||
|
// @Tags auth
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body repository.LoginRequest true "Login credentials"
|
||||||
|
// @Success 200 {object} repository.LoginResponse
|
||||||
|
// @Failure 400 {object} map[string]string
|
||||||
|
// @Failure 401 {object} map[string]string
|
||||||
|
// @Router /auth/login [post]
|
||||||
|
func (ag *AuthGroup) Login(c *gin.Context) {
|
||||||
|
var req repository.LoginRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := ag.Repo.Login(req.Login, req.Password)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, repository.ErrNotFound) {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid credentials"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to authenticate"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateToken creates a new user.
|
||||||
|
// @Summary Create user
|
||||||
|
// @Description Creates a new user with permissions
|
||||||
|
// @Tags auth
|
||||||
|
// @Accept json
|
||||||
|
// @Param request body repository.TokenCreate true "User data"
|
||||||
|
// @Success 200 {object} map[string]string
|
||||||
|
// @Failure 400 {object} map[string]string
|
||||||
|
// @Failure 401 {object} map[string]string
|
||||||
|
// @Failure 500 {object} map[string]string
|
||||||
|
// @Router /auth/token [post]
|
||||||
|
func (ag *AuthGroup) CreateToken(c *gin.Context) {
|
||||||
|
var tc repository.TokenCreate
|
||||||
|
if err := c.ShouldBindJSON(&tc); err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := ag.Repo.CreateToken(tc); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create user"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "user created"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateToken validates the current Bearer token and returns user info.
|
||||||
|
// @Summary Validate token
|
||||||
|
// @Description Check if the provided Bearer token is valid and return its permissions
|
||||||
|
// @Tags auth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} repository.Tokens
|
||||||
|
// @Failure 401 {object} map[string]string
|
||||||
|
// @Router /auth/validate [get]
|
||||||
|
func (ag *AuthGroup) ValidateToken(c *gin.Context) {
|
||||||
|
tokenVal, exists := c.Get(string(tokenContextKey))
|
||||||
|
if !exists {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authenticated"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, ok := tokenVal.(*repository.Tokens)
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token context"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListTokens returns all users.
|
||||||
|
// @Summary List users
|
||||||
|
// @Description Returns list of all users with their permissions
|
||||||
|
// @Tags auth
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {array} repository.Tokens
|
||||||
|
// @Failure 500 {object} map[string]string
|
||||||
|
// @Router /auth/tokens [get]
|
||||||
|
func (ag *AuthGroup) ListTokens(c *gin.Context) {
|
||||||
|
tokens, err := ag.Repo.ListTokens()
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list users"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusOK, tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteToken deletes a user by login from URL path.
|
||||||
|
// @Summary Delete user
|
||||||
|
// @Description Deletes a user by their login
|
||||||
|
// @Tags auth
|
||||||
|
// @Param login path string true "Login of the user to delete"
|
||||||
|
// @Success 200 {object} map[string]string
|
||||||
|
// @Failure 400 {object} map[string]string
|
||||||
|
// @Failure 500 {object} map[string]string
|
||||||
|
// @Router /auth/tokens/:login [delete]
|
||||||
|
func (ag *AuthGroup) DeleteToken(c *gin.Context) {
|
||||||
|
login := c.Param("login")
|
||||||
|
if login == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, gin.H{"error": "login required"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ag.Repo.DeleteTokenByLogin(login); err != nil {
|
||||||
|
if errors.Is(err, repository.ErrNotFound) {
|
||||||
|
c.JSON(http.StatusNotFound, gin.H{"error": "user not found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete user"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "user deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteMyToken deletes the current user's account.
|
||||||
|
// @Summary Delete my account
|
||||||
|
// @Description Deletes the current authenticated user
|
||||||
|
// @Tags auth
|
||||||
|
// @Success 200 {object} map[string]string
|
||||||
|
// @Failure 401 {object} map[string]string
|
||||||
|
// @Failure 500 {object} map[string]string
|
||||||
|
// @Router /auth/token [delete]
|
||||||
|
func (ag *AuthGroup) DeleteMyToken(c *gin.Context) {
|
||||||
|
tokenVal, exists := c.Get(string(tokenContextKey))
|
||||||
|
if !exists {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authenticated"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, ok := tokenVal.(*repository.Tokens)
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token context"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ag.Repo.DeleteToken(token.Token); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete account"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "account deleted"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// getTokenFromHeader extracts the Bearer token from the Authorization header.
|
||||||
|
func getTokenFromHeader(c *gin.Context) string {
|
||||||
|
auth := c.GetHeader("Authorization")
|
||||||
|
if auth == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
parts := strings.SplitN(auth, " ", 2)
|
||||||
|
if len(parts) != 2 || !strings.EqualFold(parts[0], "bearer") {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return parts[1]
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.d3m0k1d.ru/d3m0k1d/HellreigN/backend/internal/repository"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TokenContextKey is the context key for storing authenticated token info.
|
||||||
|
type TokenContextKey string
|
||||||
|
|
||||||
|
const tokenContextKey TokenContextKey = "token"
|
||||||
|
|
||||||
|
// AuthMiddleware validates that a Bearer token exists and is valid.
|
||||||
|
// It stores the token info in the context for later use.
|
||||||
|
// Returns 401 if token is missing or invalid.
|
||||||
|
func (ag *AuthGroup) AuthMiddleware() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
token := getTokenFromHeader(c)
|
||||||
|
if token == "" {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "missing authorization header"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up user by token value
|
||||||
|
tokens, err := ag.Repo.GetToken(token)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid token"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Set(string(tokenContextKey), tokens)
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequirePermission is a generic permission checker.
|
||||||
|
func RequirePermission(check func(*repository.Tokens) bool) gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
tokenVal, exists := c.Get(string(tokenContextKey))
|
||||||
|
if !exists {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "authentication required"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, ok := tokenVal.(*repository.Tokens)
|
||||||
|
if !ok {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "invalid token context"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !check(token) {
|
||||||
|
c.JSON(http.StatusForbidden, gin.H{"error": "insufficient permissions"})
|
||||||
|
c.Abort()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireView requires permission_view.
|
||||||
|
func RequireView() gin.HandlerFunc {
|
||||||
|
return RequirePermission(func(t *repository.Tokens) bool {
|
||||||
|
return t.PermissionView
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireManageAgent requires permission_manage_agent.
|
||||||
|
func RequireManageAgent() gin.HandlerFunc {
|
||||||
|
return RequirePermission(func(t *repository.Tokens) bool {
|
||||||
|
return t.PermissionManage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequireAdmin requires permission_admin.
|
||||||
|
func RequireAdmin() gin.HandlerFunc {
|
||||||
|
return RequirePermission(func(t *repository.Tokens) bool {
|
||||||
|
return t.PermissionAdmin
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -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
|
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 {
|
type Repository struct {
|
||||||
DB *sql.DB
|
DB *sql.DB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New creates a new Repository.
|
||||||
func New(db *sql.DB) *Repository {
|
func New(db *sql.DB) *Repository {
|
||||||
return &Repository{DB: db}
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,11 +5,12 @@ const CreateSqlite = `
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL,
|
name TEXT NOT NULL,
|
||||||
last_name TEXT NOT NULL,
|
last_name TEXT NOT NULL,
|
||||||
login TEXT NOT NULL,
|
login TEXT NOT NULL UNIQUE,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
|
token TEXT NOT NULL UNIQUE,
|
||||||
permission_view BOOL NOT NULL,
|
permission_view BOOL NOT NULL,
|
||||||
permission_manage_agent BOOL NOT NULL,
|
permission_manage_agent BOOL NOT NULL,
|
||||||
permission_tokens BOOL NOT NULL
|
permission_admin BOOL NOT NULL
|
||||||
);
|
);
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|||||||
@@ -30,5 +30,11 @@ func Open(path string) (*sql.DB, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run migrations
|
||||||
|
if _, err := db.Exec(CreateSqlite); err != nil {
|
||||||
|
return nil, fmt.Errorf("migrate: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package initial
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
services:
|
||||||
|
backend:
|
||||||
|
|
||||||
Reference in New Issue
Block a user