feat: admin routes + better auth routing
This commit is contained in:
@ -6,7 +6,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"gitea.local/admin/hspguard/internal/apiservices"
|
"gitea.local/admin/hspguard/internal/admin"
|
||||||
"gitea.local/admin/hspguard/internal/auth"
|
"gitea.local/admin/hspguard/internal/auth"
|
||||||
"gitea.local/admin/hspguard/internal/config"
|
"gitea.local/admin/hspguard/internal/config"
|
||||||
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||||
@ -45,17 +45,15 @@ func (s *APIServer) Run() error {
|
|||||||
oauthHandler := oauth.NewOAuthHandler(s.repo, s.cfg)
|
oauthHandler := oauth.NewOAuthHandler(s.repo, s.cfg)
|
||||||
|
|
||||||
router.Route("/api/v1", func(r chi.Router) {
|
router.Route("/api/v1", func(r chi.Router) {
|
||||||
am := imiddleware.New(s.cfg)
|
authMiddleware := imiddleware.NewAuthMiddleware(s.cfg)
|
||||||
r.Use(imiddleware.WithSkipper(
|
r.Use(imiddleware.WithSkipper(
|
||||||
am.Runner,
|
authMiddleware.Runner,
|
||||||
"/api/v1/auth/login",
|
|
||||||
"/api/v1/register",
|
"/api/v1/register",
|
||||||
"/api/v1/auth/refresh",
|
|
||||||
"/api/v1/oauth/token",
|
"/api/v1/oauth/token",
|
||||||
"/api/v1/avatar",
|
"/api/v1/avatar",
|
||||||
))
|
))
|
||||||
|
|
||||||
userHandler := user.NewUserHandler(s.repo, s.storage)
|
userHandler := user.NewUserHandler(s.repo, s.storage, s.cfg)
|
||||||
userHandler.RegisterRoutes(r)
|
userHandler.RegisterRoutes(r)
|
||||||
|
|
||||||
authHandler := auth.NewAuthHandler(s.repo, s.cfg)
|
authHandler := auth.NewAuthHandler(s.repo, s.cfg)
|
||||||
@ -63,8 +61,8 @@ func (s *APIServer) Run() error {
|
|||||||
|
|
||||||
oauthHandler.RegisterRoutes(r)
|
oauthHandler.RegisterRoutes(r)
|
||||||
|
|
||||||
apiServicesHandler := apiservices.New(s.repo, s.cfg)
|
adminHandler := admin.New(s.repo, s.cfg)
|
||||||
apiServicesHandler.RegisterRoutes(r)
|
adminHandler.RegisterRoutes(r)
|
||||||
})
|
})
|
||||||
|
|
||||||
router.Get("/.well-known/jwks.json", oauthHandler.WriteJWKS)
|
router.Get("/.well-known/jwks.json", oauthHandler.WriteJWKS)
|
||||||
|
@ -1,31 +1,49 @@
|
|||||||
package apiservices
|
package admin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.local/admin/hspguard/internal/config"
|
"gitea.local/admin/hspguard/internal/config"
|
||||||
|
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||||
"gitea.local/admin/hspguard/internal/repository"
|
"gitea.local/admin/hspguard/internal/repository"
|
||||||
"gitea.local/admin/hspguard/internal/util"
|
"gitea.local/admin/hspguard/internal/util"
|
||||||
"gitea.local/admin/hspguard/internal/web"
|
"gitea.local/admin/hspguard/internal/web"
|
||||||
"github.com/go-chi/chi/v5"
|
"github.com/go-chi/chi/v5"
|
||||||
"github.com/google/uuid"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ApiServicesHandler struct {
|
type AdminHandler struct {
|
||||||
repo *repository.Queries
|
repo *repository.Queries
|
||||||
cfg *config.AppConfig
|
cfg *config.AppConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(repo *repository.Queries, cfg *config.AppConfig) *ApiServicesHandler {
|
func New(repo *repository.Queries, cfg *config.AppConfig) *AdminHandler {
|
||||||
return &ApiServicesHandler{
|
return &AdminHandler{
|
||||||
repo,
|
repo,
|
||||||
cfg,
|
cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiServicesHandler) RegisterRoutes(router chi.Router) {
|
func (h *AdminHandler) RegisterRoutes(router chi.Router) {
|
||||||
router.Post("/api-services/create", h.Add)
|
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 {
|
type AddServiceRequest struct {
|
||||||
@ -35,24 +53,7 @@ type AddServiceRequest struct {
|
|||||||
GrantTypes []string `json:"grant_types"`
|
GrantTypes []string `json:"grant_types"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *ApiServicesHandler) Add(w http.ResponseWriter, r *http.Request) {
|
func (h *AdminHandler) AddApiService(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
|
|
||||||
}
|
|
||||||
|
|
||||||
var req AddServiceRequest
|
var req AddServiceRequest
|
||||||
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(r.Body)
|
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.local/admin/hspguard/internal/config"
|
"gitea.local/admin/hspguard/internal/config"
|
||||||
|
imiddleware "gitea.local/admin/hspguard/internal/middleware"
|
||||||
"gitea.local/admin/hspguard/internal/repository"
|
"gitea.local/admin/hspguard/internal/repository"
|
||||||
"gitea.local/admin/hspguard/internal/types"
|
"gitea.local/admin/hspguard/internal/types"
|
||||||
"gitea.local/admin/hspguard/internal/util"
|
"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 {
|
if err != nil {
|
||||||
return "", "", err
|
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 {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
@ -66,9 +67,17 @@ func NewAuthHandler(repo *repository.Queries, cfg *config.AppConfig) *AuthHandle
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *AuthHandler) RegisterRoutes(api chi.Router) {
|
func (h *AuthHandler) RegisterRoutes(api chi.Router) {
|
||||||
api.Get("/auth/profile", h.getProfile)
|
api.Route("/auth", func(r chi.Router) {
|
||||||
api.Post("/auth/login", h.login)
|
r.Group(func(protected chi.Router) {
|
||||||
api.Post("/auth/refresh", h.refreshToken)
|
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) {
|
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]
|
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 {
|
if err != nil || !token.Valid {
|
||||||
http.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf("invalid token: %v", err), http.StatusUnauthorized)
|
||||||
return
|
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"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"gitea.local/admin/hspguard/internal/auth"
|
|
||||||
"gitea.local/admin/hspguard/internal/config"
|
"gitea.local/admin/hspguard/internal/config"
|
||||||
"gitea.local/admin/hspguard/internal/types"
|
"gitea.local/admin/hspguard/internal/types"
|
||||||
|
"gitea.local/admin/hspguard/internal/util"
|
||||||
"gitea.local/admin/hspguard/internal/web"
|
"gitea.local/admin/hspguard/internal/web"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ type AuthMiddleware struct {
|
|||||||
cfg *config.AppConfig
|
cfg *config.AppConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.AppConfig) *AuthMiddleware {
|
func NewAuthMiddleware(cfg *config.AppConfig) *AuthMiddleware {
|
||||||
return &AuthMiddleware{
|
return &AuthMiddleware{
|
||||||
cfg,
|
cfg,
|
||||||
}
|
}
|
||||||
@ -37,9 +37,9 @@ func (m *AuthMiddleware) Runner(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tokenStr := parts[1]
|
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 {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"gitea.local/admin/hspguard/internal/auth"
|
|
||||||
"gitea.local/admin/hspguard/internal/config"
|
"gitea.local/admin/hspguard/internal/config"
|
||||||
"gitea.local/admin/hspguard/internal/repository"
|
"gitea.local/admin/hspguard/internal/repository"
|
||||||
"gitea.local/admin/hspguard/internal/types"
|
"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) {
|
func (h *OAuthHandler) RegisterRoutes(router chi.Router) {
|
||||||
r.Post("/oauth/token", h.tokenEndpoint)
|
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) {
|
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 {
|
if err != nil {
|
||||||
web.Error(w, "failed to parse public key", http.StatusInternalServerError)
|
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 {
|
if err != nil {
|
||||||
web.Error(w, "failed to sign id token", http.StatusInternalServerError)
|
web.Error(w, "failed to sign id token", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
|
@ -11,6 +11,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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/repository"
|
||||||
"gitea.local/admin/hspguard/internal/storage"
|
"gitea.local/admin/hspguard/internal/storage"
|
||||||
"gitea.local/admin/hspguard/internal/util"
|
"gitea.local/admin/hspguard/internal/util"
|
||||||
@ -24,18 +26,26 @@ import (
|
|||||||
type UserHandler struct {
|
type UserHandler struct {
|
||||||
repo *repository.Queries
|
repo *repository.Queries
|
||||||
minio *storage.FileStorage
|
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{
|
return &UserHandler{
|
||||||
repo: repo,
|
repo,
|
||||||
minio: minio,
|
minio,
|
||||||
|
cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UserHandler) RegisterRoutes(api chi.Router) {
|
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.Post("/register", h.register)
|
||||||
api.Put("/avatar", h.uploadAvatar)
|
|
||||||
api.Get("/avatar/{avatar}", h.getAvatar)
|
api.Get("/avatar/{avatar}", h.getAvatar)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package auth
|
package util
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
@ -3,16 +3,16 @@ INSERT INTO api_services (
|
|||||||
client_id, client_secret, name, redirect_uris, scopes, grant_types
|
client_id, client_secret, name, redirect_uris, scopes, grant_types
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4, $5, $6
|
$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
|
-- 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
|
WHERE client_id = $1
|
||||||
AND is_active = true
|
AND is_active = true
|
||||||
LIMIT 1;
|
LIMIT 1;
|
||||||
|
|
||||||
-- name: ListApiServices :many
|
-- 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;
|
ORDER BY created_at DESC;
|
||||||
|
|
||||||
-- name: UpdateApiService :one
|
-- name: UpdateApiService :one
|
||||||
@ -24,7 +24,7 @@ SET
|
|||||||
grant_types = $5,
|
grant_types = $5,
|
||||||
updated_at = NOW()
|
updated_at = NOW()
|
||||||
WHERE client_id = $1
|
WHERE client_id = $1
|
||||||
RETURNING *;
|
RETURNING id,client_id,name,redirect_uris,scopes,grant_types,created_at,updated_at,is_active;
|
||||||
|
|
||||||
-- name: DeactivateApiService :exec
|
-- name: DeactivateApiService :exec
|
||||||
UPDATE api_services
|
UPDATE api_services
|
||||||
@ -36,4 +36,4 @@ WHERE client_id = $1;
|
|||||||
UPDATE api_services
|
UPDATE api_services
|
||||||
SET client_secret = $2,
|
SET client_secret = $2,
|
||||||
updated_at = NOW()
|
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