feat: admin routes + better auth routing
This commit is contained in:
@ -6,7 +6,7 @@ import (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/apiservices"
|
||||
"gitea.local/admin/hspguard/internal/admin"
|
||||
"gitea.local/admin/hspguard/internal/auth"
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||
@ -45,17 +45,15 @@ func (s *APIServer) Run() error {
|
||||
oauthHandler := oauth.NewOAuthHandler(s.repo, s.cfg)
|
||||
|
||||
router.Route("/api/v1", func(r chi.Router) {
|
||||
am := imiddleware.New(s.cfg)
|
||||
authMiddleware := imiddleware.NewAuthMiddleware(s.cfg)
|
||||
r.Use(imiddleware.WithSkipper(
|
||||
am.Runner,
|
||||
"/api/v1/auth/login",
|
||||
authMiddleware.Runner,
|
||||
"/api/v1/register",
|
||||
"/api/v1/auth/refresh",
|
||||
"/api/v1/oauth/token",
|
||||
"/api/v1/avatar",
|
||||
))
|
||||
|
||||
userHandler := user.NewUserHandler(s.repo, s.storage)
|
||||
userHandler := user.NewUserHandler(s.repo, s.storage, s.cfg)
|
||||
userHandler.RegisterRoutes(r)
|
||||
|
||||
authHandler := auth.NewAuthHandler(s.repo, s.cfg)
|
||||
@ -63,8 +61,8 @@ func (s *APIServer) Run() error {
|
||||
|
||||
oauthHandler.RegisterRoutes(r)
|
||||
|
||||
apiServicesHandler := apiservices.New(s.repo, s.cfg)
|
||||
apiServicesHandler.RegisterRoutes(r)
|
||||
adminHandler := admin.New(s.repo, s.cfg)
|
||||
adminHandler.RegisterRoutes(r)
|
||||
})
|
||||
|
||||
router.Get("/.well-known/jwks.json", oauthHandler.WriteJWKS)
|
||||
|
@ -1,31 +1,49 @@
|
||||
package apiservices
|
||||
package admin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/util"
|
||||
"gitea.local/admin/hspguard/internal/web"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type ApiServicesHandler struct {
|
||||
type AdminHandler struct {
|
||||
repo *repository.Queries
|
||||
cfg *config.AppConfig
|
||||
}
|
||||
|
||||
func New(repo *repository.Queries, cfg *config.AppConfig) *ApiServicesHandler {
|
||||
return &ApiServicesHandler{
|
||||
func New(repo *repository.Queries, cfg *config.AppConfig) *AdminHandler {
|
||||
return &AdminHandler{
|
||||
repo,
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *ApiServicesHandler) RegisterRoutes(router chi.Router) {
|
||||
router.Post("/api-services/create", h.Add)
|
||||
func (h *AdminHandler) RegisterRoutes(router chi.Router) {
|
||||
router.Route("/admin", func(r chi.Router) {
|
||||
authMiddleware := imiddleware.NewAuthMiddleware(h.cfg)
|
||||
adminMiddleware := imiddleware.NewAdminMiddleware(h.repo)
|
||||
r.Use(authMiddleware.Runner, adminMiddleware.Runner)
|
||||
|
||||
r.Post("/api-services/create", h.AddApiService)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) GetApiServices(w http.ResponseWriter, r *http.Request) {
|
||||
services, err := h.repo.ListApiServices(r.Context())
|
||||
if err != nil {
|
||||
log.Println("ERR: Failed to list api services from db:", err)
|
||||
web.Error(w, "failed to get api services", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
type AddServiceRequest struct {
|
||||
@ -35,24 +53,7 @@ type AddServiceRequest struct {
|
||||
GrantTypes []string `json:"grant_types"`
|
||||
}
|
||||
|
||||
func (h *ApiServicesHandler) Add(w http.ResponseWriter, r *http.Request) {
|
||||
userId, ok := util.GetRequestUserId(r.Context())
|
||||
if !ok {
|
||||
web.Error(w, "failed to get user id from auth session", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := h.repo.FindUserId(r.Context(), uuid.MustParse(userId))
|
||||
if err != nil {
|
||||
web.Error(w, "user with provided id does not exist", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsAdmin {
|
||||
web.Error(w, "you cannot create api services", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
func (h *AdminHandler) AddApiService(w http.ResponseWriter, r *http.Request) {
|
||||
var req AddServiceRequest
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/types"
|
||||
"gitea.local/admin/hspguard/internal/util"
|
||||
@ -34,7 +35,7 @@ func (h *AuthHandler) signTokens(user *repository.User) (string, string, error)
|
||||
},
|
||||
}
|
||||
|
||||
accessToken, err := SignJwtToken(accessClaims, h.cfg.Jwt.PrivateKey)
|
||||
accessToken, err := util.SignJwtToken(accessClaims, h.cfg.Jwt.PrivateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@ -50,7 +51,7 @@ func (h *AuthHandler) signTokens(user *repository.User) (string, string, error)
|
||||
},
|
||||
}
|
||||
|
||||
refreshToken, err := SignJwtToken(refreshClaims, h.cfg.Jwt.PrivateKey)
|
||||
refreshToken, err := util.SignJwtToken(refreshClaims, h.cfg.Jwt.PrivateKey)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
@ -66,9 +67,17 @@ func NewAuthHandler(repo *repository.Queries, cfg *config.AppConfig) *AuthHandle
|
||||
}
|
||||
|
||||
func (h *AuthHandler) RegisterRoutes(api chi.Router) {
|
||||
api.Get("/auth/profile", h.getProfile)
|
||||
api.Post("/auth/login", h.login)
|
||||
api.Post("/auth/refresh", h.refreshToken)
|
||||
api.Route("/auth", func(r chi.Router) {
|
||||
r.Group(func(protected chi.Router) {
|
||||
authMiddleware := imiddleware.NewAuthMiddleware(h.cfg)
|
||||
protected.Use(authMiddleware.Runner)
|
||||
|
||||
protected.Get("/profile", h.getProfile)
|
||||
})
|
||||
|
||||
r.Post("/login", h.login)
|
||||
r.Post("/refresh", h.refreshToken)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *AuthHandler) refreshToken(w http.ResponseWriter, r *http.Request) {
|
||||
@ -85,7 +94,7 @@ func (h *AuthHandler) refreshToken(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
tokenStr := parts[1]
|
||||
token, userClaims, err := VerifyToken(tokenStr, h.cfg.Jwt.PublicKey)
|
||||
token, userClaims, err := util.VerifyToken(tokenStr, h.cfg.Jwt.PublicKey)
|
||||
if err != nil || !token.Valid {
|
||||
http.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized)
|
||||
return
|
||||
|
47
internal/middleware/admin.go
Normal file
47
internal/middleware/admin.go
Normal file
@ -0,0 +1,47 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/util"
|
||||
"gitea.local/admin/hspguard/internal/web"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type AdminMiddleware struct {
|
||||
repo *repository.Queries
|
||||
}
|
||||
|
||||
func NewAdminMiddleware(repo *repository.Queries) *AdminMiddleware {
|
||||
return &AdminMiddleware{
|
||||
repo,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *AdminMiddleware) Runner(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
userId, ok := util.GetRequestUserId(r.Context())
|
||||
if !ok {
|
||||
log.Println("ERR: Could not get user id from request")
|
||||
web.Error(w, "not authenticated", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := m.repo.FindUserId(r.Context(), uuid.MustParse(userId))
|
||||
if err != nil {
|
||||
log.Println("ERR: User with provided id does not exist:", userId)
|
||||
web.Error(w, "not authenticated", http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
if !user.IsAdmin {
|
||||
log.Println("INFO: User is not admin")
|
||||
web.Error(w, "no priviligies to access this resource", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
@ -6,9 +6,9 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/auth"
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
"gitea.local/admin/hspguard/internal/types"
|
||||
"gitea.local/admin/hspguard/internal/util"
|
||||
"gitea.local/admin/hspguard/internal/web"
|
||||
)
|
||||
|
||||
@ -16,7 +16,7 @@ type AuthMiddleware struct {
|
||||
cfg *config.AppConfig
|
||||
}
|
||||
|
||||
func New(cfg *config.AppConfig) *AuthMiddleware {
|
||||
func NewAuthMiddleware(cfg *config.AppConfig) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
cfg,
|
||||
}
|
||||
@ -37,9 +37,9 @@ func (m *AuthMiddleware) Runner(next http.Handler) http.Handler {
|
||||
}
|
||||
|
||||
tokenStr := parts[1]
|
||||
token, userClaims, err := auth.VerifyToken(tokenStr, m.cfg.Jwt.PublicKey)
|
||||
token, userClaims, err := util.VerifyToken(tokenStr, m.cfg.Jwt.PublicKey)
|
||||
if err != nil || !token.Valid {
|
||||
http.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized)
|
||||
web.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/auth"
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/types"
|
||||
@ -32,14 +31,16 @@ func NewOAuthHandler(repo *repository.Queries, cfg *config.AppConfig) *OAuthHand
|
||||
}
|
||||
}
|
||||
|
||||
func (h *OAuthHandler) RegisterRoutes(r chi.Router) {
|
||||
r.Post("/oauth/token", h.tokenEndpoint)
|
||||
func (h *OAuthHandler) RegisterRoutes(router chi.Router) {
|
||||
router.Route("/oauth", func(r chi.Router) {
|
||||
r.Post("/token", h.tokenEndpoint)
|
||||
|
||||
r.Post("/oauth/code", h.getAuthCode)
|
||||
r.Post("/code", h.getAuthCode)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *OAuthHandler) WriteJWKS(w http.ResponseWriter, r *http.Request) {
|
||||
pubKey, err := auth.ParseBase64PublicKey(h.cfg.Jwt.PublicKey)
|
||||
pubKey, err := util.ParseBase64PublicKey(h.cfg.Jwt.PublicKey)
|
||||
if err != nil {
|
||||
web.Error(w, "failed to parse public key", http.StatusInternalServerError)
|
||||
}
|
||||
@ -207,7 +208,7 @@ func (h *OAuthHandler) tokenEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
},
|
||||
}
|
||||
|
||||
idToken, err := auth.SignJwtToken(claims, h.cfg.Jwt.PrivateKey)
|
||||
idToken, err := util.SignJwtToken(claims, h.cfg.Jwt.PrivateKey)
|
||||
if err != nil {
|
||||
web.Error(w, "failed to sign id token", http.StatusInternalServerError)
|
||||
return
|
||||
|
@ -11,6 +11,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.local/admin/hspguard/internal/config"
|
||||
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||
"gitea.local/admin/hspguard/internal/repository"
|
||||
"gitea.local/admin/hspguard/internal/storage"
|
||||
"gitea.local/admin/hspguard/internal/util"
|
||||
@ -24,18 +26,26 @@ import (
|
||||
type UserHandler struct {
|
||||
repo *repository.Queries
|
||||
minio *storage.FileStorage
|
||||
cfg *config.AppConfig
|
||||
}
|
||||
|
||||
func NewUserHandler(repo *repository.Queries, minio *storage.FileStorage) *UserHandler {
|
||||
func NewUserHandler(repo *repository.Queries, minio *storage.FileStorage, cfg *config.AppConfig) *UserHandler {
|
||||
return &UserHandler{
|
||||
repo: repo,
|
||||
minio: minio,
|
||||
repo,
|
||||
minio,
|
||||
cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *UserHandler) RegisterRoutes(api chi.Router) {
|
||||
api.Group(func(protected chi.Router) {
|
||||
authMiddleware := imiddleware.NewAuthMiddleware(h.cfg)
|
||||
protected.Use(authMiddleware.Runner)
|
||||
|
||||
protected.Put("/avatar", h.uploadAvatar)
|
||||
})
|
||||
|
||||
api.Post("/register", h.register)
|
||||
api.Put("/avatar", h.uploadAvatar)
|
||||
api.Get("/avatar/{avatar}", h.getAvatar)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package auth
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
@ -3,16 +3,16 @@ INSERT INTO api_services (
|
||||
client_id, client_secret, name, redirect_uris, scopes, grant_types
|
||||
) VALUES (
|
||||
$1, $2, $3, $4, $5, $6
|
||||
) RETURNING *;
|
||||
) RETURNING id,client_id,name,redirect_uris,scopes,grant_types,created_at,updated_at,is_active;
|
||||
|
||||
-- name: GetApiServiceCID :one
|
||||
SELECT * FROM api_services
|
||||
SELECT id,client_id,name,redirect_uris,scopes,grant_types,created_at,updated_at,is_active FROM api_services
|
||||
WHERE client_id = $1
|
||||
AND is_active = true
|
||||
LIMIT 1;
|
||||
|
||||
-- name: ListApiServices :many
|
||||
SELECT * FROM api_services
|
||||
SELECT id,client_id,name,redirect_uris,scopes,grant_types,created_at,updated_at,is_active FROM api_services
|
||||
ORDER BY created_at DESC;
|
||||
|
||||
-- name: UpdateApiService :one
|
||||
@ -24,7 +24,7 @@ SET
|
||||
grant_types = $5,
|
||||
updated_at = NOW()
|
||||
WHERE client_id = $1
|
||||
RETURNING *;
|
||||
RETURNING id,client_id,name,redirect_uris,scopes,grant_types,created_at,updated_at,is_active;
|
||||
|
||||
-- name: DeactivateApiService :exec
|
||||
UPDATE api_services
|
||||
@ -36,4 +36,4 @@ WHERE client_id = $1;
|
||||
UPDATE api_services
|
||||
SET client_secret = $2,
|
||||
updated_at = NOW()
|
||||
WHERE client_id = $1;
|
||||
WHERE client_id = $1;
|
||||
|
8
web/src/api/admin/apiServices.ts
Normal file
8
web/src/api/admin/apiServices.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { axios } from ".."
|
||||
|
||||
|
||||
export interface FetchApiServicesRequest {}
|
||||
|
||||
export const fetchApiServices = async (req: FetchApiServicesRequest) => {
|
||||
const response = await axios.get()
|
||||
}
|
Reference in New Issue
Block a user