mirror of
https://github.com/netbirdio/netbird.git
synced 2026-04-16 07:16:38 +00:00
185 lines
5.7 KiB
Go
185 lines
5.7 KiB
Go
package proxytoken
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
|
|
nbcontext "github.com/netbirdio/netbird/management/server/context"
|
|
"github.com/netbirdio/netbird/management/server/permissions"
|
|
"github.com/netbirdio/netbird/management/server/permissions/modules"
|
|
"github.com/netbirdio/netbird/management/server/permissions/operations"
|
|
"github.com/netbirdio/netbird/management/server/store"
|
|
"github.com/netbirdio/netbird/management/server/types"
|
|
"github.com/netbirdio/netbird/shared/management/http/api"
|
|
"github.com/netbirdio/netbird/shared/management/http/util"
|
|
)
|
|
|
|
type handler struct {
|
|
store store.Store
|
|
permissionsManager permissions.Manager
|
|
}
|
|
|
|
func RegisterEndpoints(s store.Store, permissionsManager permissions.Manager, router *mux.Router) {
|
|
h := &handler{store: s, permissionsManager: permissionsManager}
|
|
router.HandleFunc("/reverse-proxies/proxy-tokens", h.listTokens).Methods("GET", "OPTIONS")
|
|
router.HandleFunc("/reverse-proxies/proxy-tokens", h.createToken).Methods("POST", "OPTIONS")
|
|
router.HandleFunc("/reverse-proxies/proxy-tokens/{tokenId}", h.revokeToken).Methods("DELETE", "OPTIONS")
|
|
}
|
|
|
|
func (h *handler) createToken(w http.ResponseWriter, r *http.Request) {
|
|
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
|
if err != nil {
|
|
util.WriteError(r.Context(), err, w)
|
|
return
|
|
}
|
|
|
|
ok, err := h.permissionsManager.ValidateUserPermissions(r.Context(), userAuth.AccountId, userAuth.UserId, modules.Services, operations.Create)
|
|
if err != nil {
|
|
util.WriteErrorResponse("failed to validate permissions", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
if !ok {
|
|
util.WriteErrorResponse("permission denied", http.StatusForbidden, w)
|
|
return
|
|
}
|
|
|
|
var req api.ProxyTokenRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
util.WriteErrorResponse("couldn't parse JSON request", http.StatusBadRequest, w)
|
|
return
|
|
}
|
|
|
|
if req.Name == "" || len(req.Name) > 255 {
|
|
util.WriteErrorResponse("name is required and must be at most 255 characters", http.StatusBadRequest, w)
|
|
return
|
|
}
|
|
|
|
var expiresIn time.Duration
|
|
if req.ExpiresIn != nil && *req.ExpiresIn > 0 {
|
|
expiresIn = time.Duration(*req.ExpiresIn) * time.Second
|
|
}
|
|
|
|
accountID := userAuth.AccountId
|
|
generated, err := types.CreateNewProxyAccessToken(req.Name, expiresIn, &accountID, userAuth.UserId)
|
|
if err != nil {
|
|
util.WriteErrorResponse("failed to generate token", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
|
|
if err := h.store.SaveProxyAccessToken(r.Context(), &generated.ProxyAccessToken); err != nil {
|
|
util.WriteErrorResponse("failed to save token", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
|
|
resp := toProxyTokenCreatedResponse(generated)
|
|
util.WriteJSONObject(r.Context(), w, resp)
|
|
}
|
|
|
|
func (h *handler) listTokens(w http.ResponseWriter, r *http.Request) {
|
|
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
|
if err != nil {
|
|
util.WriteError(r.Context(), err, w)
|
|
return
|
|
}
|
|
|
|
ok, err := h.permissionsManager.ValidateUserPermissions(r.Context(), userAuth.AccountId, userAuth.UserId, modules.Services, operations.Read)
|
|
if err != nil {
|
|
util.WriteErrorResponse("failed to validate permissions", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
if !ok {
|
|
util.WriteErrorResponse("permission denied", http.StatusForbidden, w)
|
|
return
|
|
}
|
|
|
|
tokens, err := h.store.GetProxyAccessTokensByAccountID(r.Context(), store.LockingStrengthNone, userAuth.AccountId)
|
|
if err != nil {
|
|
util.WriteErrorResponse("failed to list tokens", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
|
|
resp := make([]api.ProxyToken, 0, len(tokens))
|
|
for _, token := range tokens {
|
|
resp = append(resp, toProxyTokenResponse(token))
|
|
}
|
|
|
|
util.WriteJSONObject(r.Context(), w, resp)
|
|
}
|
|
|
|
func (h *handler) revokeToken(w http.ResponseWriter, r *http.Request) {
|
|
userAuth, err := nbcontext.GetUserAuthFromContext(r.Context())
|
|
if err != nil {
|
|
util.WriteError(r.Context(), err, w)
|
|
return
|
|
}
|
|
|
|
ok, err := h.permissionsManager.ValidateUserPermissions(r.Context(), userAuth.AccountId, userAuth.UserId, modules.Services, operations.Delete)
|
|
if err != nil {
|
|
util.WriteErrorResponse("failed to validate permissions", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
if !ok {
|
|
util.WriteErrorResponse("permission denied", http.StatusForbidden, w)
|
|
return
|
|
}
|
|
|
|
tokenID := mux.Vars(r)["tokenId"]
|
|
if tokenID == "" {
|
|
util.WriteErrorResponse("token ID is required", http.StatusBadRequest, w)
|
|
return
|
|
}
|
|
|
|
token, err := h.store.GetProxyAccessTokenByID(r.Context(), store.LockingStrengthNone, tokenID)
|
|
if err != nil {
|
|
util.WriteErrorResponse("token not found", http.StatusNotFound, w)
|
|
return
|
|
}
|
|
|
|
if token.AccountID == nil || *token.AccountID != userAuth.AccountId {
|
|
util.WriteErrorResponse("token not found", http.StatusNotFound, w)
|
|
return
|
|
}
|
|
|
|
if err := h.store.RevokeProxyAccessToken(r.Context(), tokenID); err != nil {
|
|
util.WriteErrorResponse("failed to revoke token", http.StatusInternalServerError, w)
|
|
return
|
|
}
|
|
|
|
util.WriteJSONObject(r.Context(), w, util.EmptyObject{})
|
|
}
|
|
|
|
func toProxyTokenResponse(token *types.ProxyAccessToken) api.ProxyToken {
|
|
resp := api.ProxyToken{
|
|
Id: token.ID,
|
|
Name: token.Name,
|
|
Revoked: token.Revoked,
|
|
}
|
|
if !token.CreatedAt.IsZero() {
|
|
resp.CreatedAt = token.CreatedAt
|
|
}
|
|
if token.ExpiresAt != nil {
|
|
resp.ExpiresAt = token.ExpiresAt
|
|
}
|
|
if token.LastUsed != nil {
|
|
resp.LastUsed = token.LastUsed
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func toProxyTokenCreatedResponse(generated *types.ProxyAccessTokenGenerated) api.ProxyTokenCreated {
|
|
base := toProxyTokenResponse(&generated.ProxyAccessToken)
|
|
plainToken := string(generated.PlainToken)
|
|
return api.ProxyTokenCreated{
|
|
Id: base.Id,
|
|
Name: base.Name,
|
|
CreatedAt: base.CreatedAt,
|
|
ExpiresAt: base.ExpiresAt,
|
|
LastUsed: base.LastUsed,
|
|
Revoked: base.Revoked,
|
|
PlainToken: plainToken,
|
|
}
|
|
}
|