mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-15 09:22:44 +00:00
feat(serviceaccount): deprecate the api keys
This commit is contained in:
@@ -12,10 +12,10 @@ import (
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/http/render"
|
||||
"github.com/SigNoz/signoz/pkg/modules/user"
|
||||
basemodel "github.com/SigNoz/signoz/pkg/query-service/model"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/roletypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/serviceaccounttypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
"go.uber.org/zap"
|
||||
@@ -49,7 +49,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), claims.OrgID, cloudProvider)
|
||||
apiKey, apiErr := ah.getOrCreateCloudIntegrationAPIKey(r.Context(), claims.OrgID, cloudProvider)
|
||||
if apiErr != nil {
|
||||
RespondError(w, basemodel.WrapApiError(
|
||||
apiErr, "couldn't provision PAT for cloud integration:",
|
||||
@@ -109,32 +109,25 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
||||
ah.Respond(w, result)
|
||||
}
|
||||
|
||||
func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId string, cloudProvider string) (
|
||||
func (ah *APIHandler) getOrCreateCloudIntegrationAPIKey(ctx context.Context, orgId string, cloudProvider string) (
|
||||
string, *basemodel.ApiError,
|
||||
) {
|
||||
integrationPATName := fmt.Sprintf("%s integration", cloudProvider)
|
||||
|
||||
integrationUser, apiErr := ah.getOrCreateCloudIntegrationUser(ctx, orgId, cloudProvider)
|
||||
integrationServiceAccount, apiErr := ah.getOrCreateCloudIntegrationServiceAccount(ctx, orgId, cloudProvider)
|
||||
if apiErr != nil {
|
||||
return "", apiErr
|
||||
}
|
||||
|
||||
orgIdUUID, err := valuer.NewUUID(orgId)
|
||||
keys, err := ah.Signoz.Modules.ServiceAccount.ListFactorAPIKey(ctx, integrationServiceAccount.ID)
|
||||
if err != nil {
|
||||
return "", basemodel.InternalError(fmt.Errorf(
|
||||
"couldn't parse orgId: %w", err,
|
||||
"couldn't list api keys: %w", err,
|
||||
))
|
||||
}
|
||||
|
||||
allPats, err := ah.Signoz.Modules.User.ListAPIKeys(ctx, orgIdUUID)
|
||||
if err != nil {
|
||||
return "", basemodel.InternalError(fmt.Errorf(
|
||||
"couldn't list PATs: %w", err,
|
||||
))
|
||||
}
|
||||
for _, p := range allPats {
|
||||
if p.UserID == integrationUser.ID && p.Name == integrationPATName {
|
||||
return p.Token, nil
|
||||
for _, key := range keys {
|
||||
if key.Name == integrationPATName {
|
||||
return key.Key, nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,46 +136,35 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
||||
zap.String("cloudProvider", cloudProvider),
|
||||
)
|
||||
|
||||
newPAT, err := types.NewStorableAPIKey(
|
||||
integrationPATName,
|
||||
integrationUser.ID,
|
||||
types.RoleViewer,
|
||||
0,
|
||||
)
|
||||
apiKey, err := integrationServiceAccount.NewFactorAPIKey(integrationPATName, 0)
|
||||
if err != nil {
|
||||
return "", basemodel.InternalError(fmt.Errorf(
|
||||
"couldn't create cloud integration PAT: %w", err,
|
||||
))
|
||||
}
|
||||
|
||||
err = ah.Signoz.Modules.User.CreateAPIKey(ctx, newPAT)
|
||||
err = ah.Signoz.Modules.ServiceAccount.CreateFactorAPIKey(ctx, apiKey)
|
||||
if err != nil {
|
||||
return "", basemodel.InternalError(fmt.Errorf(
|
||||
"couldn't create cloud integration PAT: %w", err,
|
||||
"couldn't create cloud integration api key: %w", err,
|
||||
))
|
||||
}
|
||||
return newPAT.Token, nil
|
||||
return apiKey.Key, nil
|
||||
}
|
||||
|
||||
func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
||||
func (ah *APIHandler) getOrCreateCloudIntegrationServiceAccount(
|
||||
ctx context.Context, orgId string, cloudProvider string,
|
||||
) (*types.User, *basemodel.ApiError) {
|
||||
cloudIntegrationUserName := fmt.Sprintf("%s-integration", cloudProvider)
|
||||
email := valuer.MustNewEmail(fmt.Sprintf("%s@signoz.io", cloudIntegrationUserName))
|
||||
) (*serviceaccounttypes.ServiceAccount, *basemodel.ApiError) {
|
||||
serviceAccountName := fmt.Sprintf("%s-integration", cloudProvider)
|
||||
email := valuer.MustNewEmail(fmt.Sprintf("%s@signoz.io", serviceAccountName))
|
||||
|
||||
cloudIntegrationUser, err := types.NewUser(cloudIntegrationUserName, email, types.RoleViewer, valuer.MustNewUUID(orgId), types.UserStatusActive)
|
||||
serviceAccount := serviceaccounttypes.NewServiceAccount(serviceAccountName, email, []string{roletypes.SigNozViewerRoleName}, serviceaccounttypes.StatusActive, valuer.MustNewUUID(orgId))
|
||||
serviceAccount, err := ah.Signoz.Modules.ServiceAccount.GetOrCreate(ctx, serviceAccount)
|
||||
if err != nil {
|
||||
return nil, basemodel.InternalError(fmt.Errorf("couldn't create cloud integration user: %w", err))
|
||||
return nil, basemodel.InternalError(fmt.Errorf("couldn't look for integration service account: %w", err))
|
||||
}
|
||||
|
||||
password := types.MustGenerateFactorPassword(cloudIntegrationUser.ID.StringValue())
|
||||
|
||||
cloudIntegrationUser, err = ah.Signoz.Modules.User.GetOrCreateUser(ctx, cloudIntegrationUser, user.WithFactorPassword(password))
|
||||
if err != nil {
|
||||
return nil, basemodel.InternalError(fmt.Errorf("couldn't look for integration user: %w", err))
|
||||
}
|
||||
|
||||
return cloudIntegrationUser, nil
|
||||
return serviceAccount, nil
|
||||
}
|
||||
|
||||
func (ah *APIHandler) getIngestionUrlAndSigNozAPIUrl(ctx context.Context, licenseKey string) (
|
||||
|
||||
@@ -111,74 +111,6 @@ func (provider *provider) addUserRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/pats", handler.New(provider.authZ.AdminAccess(provider.userHandler.CreateAPIKey), handler.OpenAPIDef{
|
||||
ID: "CreateAPIKey",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Create api key",
|
||||
Description: "This endpoint creates an api key",
|
||||
Request: new(types.PostableAPIKey),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(types.GettableAPIKey),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/pats", handler.New(provider.authZ.AdminAccess(provider.userHandler.ListAPIKeys), handler.OpenAPIDef{
|
||||
ID: "ListAPIKeys",
|
||||
Tags: []string{"users"},
|
||||
Summary: "List api keys",
|
||||
Description: "This endpoint lists all api keys",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: make([]*types.GettableAPIKey, 0),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/pats/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.UpdateAPIKey), handler.OpenAPIDef{
|
||||
ID: "UpdateAPIKey",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Update api key",
|
||||
Description: "This endpoint updates an api key",
|
||||
Request: new(types.StorableAPIKey),
|
||||
RequestContentType: "application/json",
|
||||
Response: nil,
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/pats/{id}", handler.New(provider.authZ.AdminAccess(provider.userHandler.RevokeAPIKey), handler.OpenAPIDef{
|
||||
ID: "RevokeAPIKey",
|
||||
Tags: []string{"users"},
|
||||
Summary: "Revoke api key",
|
||||
Description: "This endpoint revokes an api key",
|
||||
Request: nil,
|
||||
RequestContentType: "",
|
||||
Response: nil,
|
||||
ResponseContentType: "",
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/user", handler.New(provider.authZ.AdminAccess(provider.userHandler.ListUsers), handler.OpenAPIDef{
|
||||
ID: "ListUsers",
|
||||
Tags: []string{"users"},
|
||||
|
||||
@@ -84,6 +84,24 @@ func (store *store) GetByID(ctx context.Context, id valuer.UUID) (*serviceaccoun
|
||||
return storable, nil
|
||||
}
|
||||
|
||||
func (store *store) GetByOrgIDAndName(ctx context.Context, orgID valuer.UUID, name string) (*serviceaccounttypes.StorableServiceAccount, error) {
|
||||
storable := new(serviceaccounttypes.StorableServiceAccount)
|
||||
|
||||
err := store.
|
||||
sqlstore.
|
||||
BunDBCtx(ctx).
|
||||
NewSelect().
|
||||
Model(storable).
|
||||
Where("org_id = ?", orgID).
|
||||
Where("name = ?", name).
|
||||
Scan(ctx)
|
||||
if err != nil {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(err, serviceaccounttypes.ErrCodeServiceAccountNotFound, "service account with name: %s doesn't exist in org: %s", name, orgID.String())
|
||||
}
|
||||
|
||||
return storable, nil
|
||||
}
|
||||
|
||||
func (store *store) List(ctx context.Context, orgID valuer.UUID) ([]*serviceaccounttypes.StorableServiceAccount, error) {
|
||||
storables := make([]*serviceaccounttypes.StorableServiceAccount, 0)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ type Module interface {
|
||||
// Creates a new service account for an organization.
|
||||
Create(context.Context, valuer.UUID, *serviceaccounttypes.ServiceAccount) error
|
||||
|
||||
GetOrCreate(context.Context, *serviceaccounttypes.ServiceAccount) (*serviceaccounttypes.ServiceAccount, error)
|
||||
// Gets a service account by id.
|
||||
Get(context.Context, valuer.UUID, valuer.UUID) (*serviceaccounttypes.ServiceAccount, error)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -13,7 +12,6 @@ import (
|
||||
root "github.com/SigNoz/signoz/pkg/modules/user"
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/integrationtypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
@@ -349,172 +347,3 @@ func (h *handler) ForgotPassword(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) CreateAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := new(types.PostableAPIKey)
|
||||
if err := json.NewDecoder(r.Body).Decode(req); err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to decode api key"))
|
||||
return
|
||||
}
|
||||
|
||||
apiKey, err := types.NewStorableAPIKey(
|
||||
req.Name,
|
||||
valuer.MustNewUUID(claims.UserID),
|
||||
req.Role,
|
||||
req.ExpiresInDays,
|
||||
)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = h.module.CreateAPIKey(ctx, apiKey)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
createdApiKey, err := h.module.GetAPIKey(ctx, valuer.MustNewUUID(claims.OrgID), apiKey.ID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// just corrected the status code, response is same,
|
||||
render.Success(w, http.StatusCreated, createdApiKey)
|
||||
}
|
||||
|
||||
func (h *handler) ListAPIKeys(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
apiKeys, err := h.module.ListAPIKeys(ctx, valuer.MustNewUUID(claims.OrgID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// for backward compatibility
|
||||
if len(apiKeys) == 0 {
|
||||
render.Success(w, http.StatusOK, []types.GettableAPIKey{})
|
||||
return
|
||||
}
|
||||
|
||||
result := make([]*types.GettableAPIKey, len(apiKeys))
|
||||
for i, apiKey := range apiKeys {
|
||||
result[i] = types.NewGettableAPIKeyFromStorableAPIKey(apiKey)
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusOK, result)
|
||||
|
||||
}
|
||||
|
||||
func (h *handler) UpdateAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
req := types.StorableAPIKey{}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
render.Error(w, errors.Wrapf(err, errors.TypeInvalidInput, errors.CodeInvalidInput, "failed to decode api key"))
|
||||
return
|
||||
}
|
||||
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := valuer.NewUUID(idStr)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid uuid-v7"))
|
||||
return
|
||||
}
|
||||
|
||||
//get the API Key
|
||||
existingAPIKey, err := h.module.GetAPIKey(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// get the user
|
||||
createdByUser, err := h.getter.Get(ctx, existingAPIKey.UserID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if slices.Contains(integrationtypes.AllIntegrationUserEmails, integrationtypes.IntegrationUserEmail(createdByUser.Email.String())) {
|
||||
render.Error(w, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "API Keys for integration users cannot be revoked"))
|
||||
return
|
||||
}
|
||||
|
||||
err = h.module.UpdateAPIKey(ctx, id, &req, valuer.MustNewUUID(claims.UserID))
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
func (h *handler) RevokeAPIKey(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
claims, err := authtypes.ClaimsFromContext(ctx)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
idStr := mux.Vars(r)["id"]
|
||||
id, err := valuer.NewUUID(idStr)
|
||||
if err != nil {
|
||||
render.Error(w, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "id is not a valid uuid-v7"))
|
||||
return
|
||||
}
|
||||
|
||||
//get the API Key
|
||||
existingAPIKey, err := h.module.GetAPIKey(ctx, valuer.MustNewUUID(claims.OrgID), id)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
// get the user
|
||||
createdByUser, err := h.getter.Get(ctx, existingAPIKey.UserID)
|
||||
if err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
if slices.Contains(integrationtypes.AllIntegrationUserEmails, integrationtypes.IntegrationUserEmail(createdByUser.Email.String())) {
|
||||
render.Error(w, errors.Newf(errors.TypeInvalidInput, errors.CodeInvalidInput, "API Keys for integration users cannot be revoked"))
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.module.RevokeAPIKey(ctx, id, valuer.MustNewUUID(claims.UserID)); err != nil {
|
||||
render.Error(w, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(w, http.StatusNoContent, nil)
|
||||
}
|
||||
|
||||
@@ -661,26 +661,6 @@ func (module *Module) GetOrCreateUser(ctx context.Context, user *types.User, opt
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (m *Module) CreateAPIKey(ctx context.Context, apiKey *types.StorableAPIKey) error {
|
||||
return m.store.CreateAPIKey(ctx, apiKey)
|
||||
}
|
||||
|
||||
func (m *Module) UpdateAPIKey(ctx context.Context, id valuer.UUID, apiKey *types.StorableAPIKey, updaterID valuer.UUID) error {
|
||||
return m.store.UpdateAPIKey(ctx, id, apiKey, updaterID)
|
||||
}
|
||||
|
||||
func (m *Module) ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*types.StorableAPIKeyUser, error) {
|
||||
return m.store.ListAPIKeys(ctx, orgID)
|
||||
}
|
||||
|
||||
func (m *Module) GetAPIKey(ctx context.Context, orgID, id valuer.UUID) (*types.StorableAPIKeyUser, error) {
|
||||
return m.store.GetAPIKey(ctx, orgID, id)
|
||||
}
|
||||
|
||||
func (m *Module) RevokeAPIKey(ctx context.Context, id, removedByUserID valuer.UUID) error {
|
||||
return m.store.RevokeAPIKey(ctx, id, removedByUserID)
|
||||
}
|
||||
|
||||
func (module *Module) CreateFirstUser(ctx context.Context, organization *types.Organization, name string, email valuer.Email, passwd string) (*types.User, error) {
|
||||
user, err := types.NewRootUser(name, email, organization.ID)
|
||||
if err != nil {
|
||||
@@ -734,11 +714,6 @@ func (module *Module) Collect(ctx context.Context, orgID valuer.UUID) (map[strin
|
||||
stats["user.count.pending_invite"] = counts[types.UserStatusPendingInvite]
|
||||
}
|
||||
|
||||
count, err := module.store.CountAPIKeyByOrgID(ctx, orgID)
|
||||
if err == nil {
|
||||
stats["factor.api_key.count"] = count
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package impluser
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
@@ -218,15 +217,6 @@ func (store *store) DeleteUser(ctx context.Context, orgID string, id string) err
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to delete factor password")
|
||||
}
|
||||
|
||||
// delete api keys
|
||||
_, err = tx.NewDelete().
|
||||
Model(&types.StorableAPIKey{}).
|
||||
Where("user_id = ?", id).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to delete API keys")
|
||||
}
|
||||
|
||||
// delete user_preference
|
||||
_, err = tx.NewDelete().
|
||||
Model(new(preferencetypes.StorableUserPreference)).
|
||||
@@ -457,111 +447,6 @@ func (store *store) UpdatePassword(ctx context.Context, factorPassword *types.Fa
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- API KEY ---
|
||||
func (store *store) CreateAPIKey(ctx context.Context, apiKey *types.StorableAPIKey) error {
|
||||
_, err := store.sqlstore.BunDB().NewInsert().
|
||||
Model(apiKey).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return store.sqlstore.WrapAlreadyExistsErrf(err, types.ErrAPIKeyAlreadyExists, "API key with token: %s already exists", apiKey.Token)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) UpdateAPIKey(ctx context.Context, id valuer.UUID, apiKey *types.StorableAPIKey, updaterID valuer.UUID) error {
|
||||
apiKey.UpdatedBy = updaterID.String()
|
||||
apiKey.UpdatedAt = time.Now()
|
||||
_, err := store.sqlstore.BunDB().NewUpdate().
|
||||
Model(apiKey).
|
||||
Column("role", "name", "updated_at", "updated_by").
|
||||
Where("id = ?", id).
|
||||
Where("revoked = false").
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return store.sqlstore.WrapNotFoundErrf(err, types.ErrAPIKeyNotFound, "API key with id: %s does not exist", id)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*types.StorableAPIKeyUser, error) {
|
||||
orgUserAPIKeys := new(types.OrgUserAPIKey)
|
||||
|
||||
if err := store.sqlstore.BunDB().NewSelect().
|
||||
Model(orgUserAPIKeys).
|
||||
Relation("Users").
|
||||
Relation("Users.APIKeys", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("revoked = false")
|
||||
},
|
||||
).
|
||||
Relation("Users.APIKeys.CreatedByUser").
|
||||
Relation("Users.APIKeys.UpdatedByUser").
|
||||
Where("id = ?", orgID).
|
||||
Scan(ctx); err != nil {
|
||||
return nil, errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to fetch API keys")
|
||||
}
|
||||
|
||||
// Flatten the API keys from all users
|
||||
var allAPIKeys []*types.StorableAPIKeyUser
|
||||
for _, user := range orgUserAPIKeys.Users {
|
||||
if user.APIKeys != nil {
|
||||
allAPIKeys = append(allAPIKeys, user.APIKeys...)
|
||||
}
|
||||
}
|
||||
|
||||
// sort the API keys by updated_at
|
||||
sort.Slice(allAPIKeys, func(i, j int) bool {
|
||||
return allAPIKeys[i].UpdatedAt.After(allAPIKeys[j].UpdatedAt)
|
||||
})
|
||||
|
||||
return allAPIKeys, nil
|
||||
}
|
||||
|
||||
func (store *store) RevokeAPIKey(ctx context.Context, id, revokedByUserID valuer.UUID) error {
|
||||
updatedAt := time.Now().Unix()
|
||||
_, err := store.sqlstore.BunDB().NewUpdate().
|
||||
Model(&types.StorableAPIKey{}).
|
||||
Set("revoked = ?", true).
|
||||
Set("updated_by = ?", revokedByUserID).
|
||||
Set("updated_at = ?", updatedAt).
|
||||
Where("id = ?", id).
|
||||
Exec(ctx)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, errors.TypeInternal, errors.CodeInternal, "failed to revoke API key")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (store *store) GetAPIKey(ctx context.Context, orgID, id valuer.UUID) (*types.StorableAPIKeyUser, error) {
|
||||
apiKey := new(types.OrgUserAPIKey)
|
||||
if err := store.sqlstore.BunDB().NewSelect().
|
||||
Model(apiKey).
|
||||
Relation("Users").
|
||||
Relation("Users.APIKeys", func(q *bun.SelectQuery) *bun.SelectQuery {
|
||||
return q.Where("revoked = false").Where("storable_api_key.id = ?", id).
|
||||
OrderExpr("storable_api_key.updated_at DESC").Limit(1)
|
||||
},
|
||||
).
|
||||
Relation("Users.APIKeys.CreatedByUser").
|
||||
Relation("Users.APIKeys.UpdatedByUser").
|
||||
Scan(ctx); err != nil {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(err, types.ErrAPIKeyNotFound, "API key with id: %s does not exist", id)
|
||||
}
|
||||
|
||||
// flatten the API keys
|
||||
flattenedAPIKeys := []*types.StorableAPIKeyUser{}
|
||||
for _, user := range apiKey.Users {
|
||||
if user.APIKeys != nil {
|
||||
flattenedAPIKeys = append(flattenedAPIKeys, user.APIKeys...)
|
||||
}
|
||||
}
|
||||
if len(flattenedAPIKeys) == 0 {
|
||||
return nil, store.sqlstore.WrapNotFoundErrf(errors.New(errors.TypeNotFound, errors.CodeNotFound, "API key with id: %s does not exist"), types.ErrAPIKeyNotFound, "API key with id: %s does not exist", id)
|
||||
}
|
||||
|
||||
return flattenedAPIKeys[0], nil
|
||||
}
|
||||
|
||||
func (store *store) CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {
|
||||
user := new(types.User)
|
||||
|
||||
@@ -609,24 +494,6 @@ func (store *store) CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UU
|
||||
return counts, nil
|
||||
}
|
||||
|
||||
func (store *store) CountAPIKeyByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error) {
|
||||
apiKey := new(types.StorableAPIKey)
|
||||
|
||||
count, err := store.
|
||||
sqlstore.
|
||||
BunDB().
|
||||
NewSelect().
|
||||
Model(apiKey).
|
||||
Join("JOIN users ON users.id = storable_api_key.user_id").
|
||||
Where("org_id = ?", orgID).
|
||||
Count(ctx)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return int64(count), nil
|
||||
}
|
||||
|
||||
func (store *store) RunInTx(ctx context.Context, cb func(ctx context.Context) error) error {
|
||||
return store.sqlstore.RunInTxCtx(ctx, nil, func(ctx context.Context) error {
|
||||
return cb(ctx)
|
||||
|
||||
@@ -45,13 +45,6 @@ type Module interface {
|
||||
AcceptInvite(ctx context.Context, token string, password string) (*types.User, error)
|
||||
GetInviteByToken(ctx context.Context, token string) (*types.Invite, error)
|
||||
|
||||
// API KEY
|
||||
CreateAPIKey(ctx context.Context, apiKey *types.StorableAPIKey) error
|
||||
UpdateAPIKey(ctx context.Context, id valuer.UUID, apiKey *types.StorableAPIKey, updaterID valuer.UUID) error
|
||||
ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*types.StorableAPIKeyUser, error)
|
||||
RevokeAPIKey(ctx context.Context, id, removedByUserID valuer.UUID) error
|
||||
GetAPIKey(ctx context.Context, orgID valuer.UUID, id valuer.UUID) (*types.StorableAPIKeyUser, error)
|
||||
|
||||
GetNonDeletedUserByEmailAndOrgID(ctx context.Context, email valuer.Email, orgID valuer.UUID) (*types.User, error)
|
||||
|
||||
statsreporter.StatsCollector
|
||||
@@ -106,10 +99,4 @@ type Handler interface {
|
||||
ResetPassword(http.ResponseWriter, *http.Request)
|
||||
ChangePassword(http.ResponseWriter, *http.Request)
|
||||
ForgotPassword(http.ResponseWriter, *http.Request)
|
||||
|
||||
// API KEY
|
||||
CreateAPIKey(http.ResponseWriter, *http.Request)
|
||||
ListAPIKeys(http.ResponseWriter, *http.Request)
|
||||
UpdateAPIKey(http.ResponseWriter, *http.Request)
|
||||
RevokeAPIKey(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
@@ -69,6 +69,9 @@ func (migration *addServiceAccount) Up(ctx context.Context, db *bun.DB) error {
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
indexSQLs := migration.sqlschema.Operator().CreateIndex(&sqlschema.UniqueIndex{TableName: "service_account", ColumnNames: []sqlschema.ColumnName{"name", "org_id"}})
|
||||
sqls = append(sqls, indexSQLs...)
|
||||
|
||||
tableSQLs = migration.sqlschema.Operator().CreateTable(&sqlschema.Table{
|
||||
Name: "service_account_role",
|
||||
Columns: []*sqlschema.Column{
|
||||
@@ -96,7 +99,7 @@ func (migration *addServiceAccount) Up(ctx context.Context, db *bun.DB) error {
|
||||
})
|
||||
sqls = append(sqls, tableSQLs...)
|
||||
|
||||
indexSQLs := migration.sqlschema.Operator().CreateIndex(&sqlschema.UniqueIndex{TableName: "service_account_role", ColumnNames: []sqlschema.ColumnName{"service_account_id", "role_id"}})
|
||||
indexSQLs = migration.sqlschema.Operator().CreateIndex(&sqlschema.UniqueIndex{TableName: "service_account_role", ColumnNames: []sqlschema.ColumnName{"service_account_id", "role_id"}})
|
||||
sqls = append(sqls, indexSQLs...)
|
||||
|
||||
for _, sql := range sqls {
|
||||
@@ -114,4 +117,4 @@ func (migration *addServiceAccount) Up(ctx context.Context, db *bun.DB) error {
|
||||
|
||||
func (a *addServiceAccount) Down(context.Context, *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,20 +115,19 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
_ = tx.Rollback()
|
||||
}()
|
||||
|
||||
// Step 1: Read all old API keys (skip revoked ones).
|
||||
// get all the api keys
|
||||
oldKeys := make([]*oldFactorAPIKey68, 0)
|
||||
err = tx.NewSelect().Model(&oldKeys).Where("revoked = ?", false).Scan(ctx)
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return err
|
||||
}
|
||||
|
||||
// Step 2: Collect unique user IDs from old keys.
|
||||
// get all the unique users
|
||||
userIDs := make(map[string]struct{})
|
||||
for _, key := range oldKeys {
|
||||
userIDs[key.UserID] = struct{}{}
|
||||
}
|
||||
|
||||
// Step 3: Load users that own API keys.
|
||||
userIDList := make([]string, 0, len(userIDs))
|
||||
for uid := range userIDs {
|
||||
userIDList = append(userIDList, uid)
|
||||
@@ -146,8 +145,7 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Load managed roles to map old role names to role IDs.
|
||||
// Build a lookup of (org_id, role_name) -> role_id.
|
||||
// get the role ids
|
||||
type orgRoleKey struct {
|
||||
OrgID string
|
||||
RoleName string
|
||||
@@ -173,47 +171,29 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5: Create a service account per user that has API keys, and collect migrated data.
|
||||
// One service account per user, all their API keys point to it.
|
||||
userIDToServiceAccountID := make(map[string]string)
|
||||
serviceAccounts := make([]*newServiceAccount68, 0)
|
||||
serviceAccountRoles := make([]*newServiceAccountRole68, 0)
|
||||
newKeys := make([]*newFactorAPIKey68, 0)
|
||||
|
||||
// Track which (serviceAccountID, roleID) pairs we've already added.
|
||||
type saRoleKey struct {
|
||||
ServiceAccountID string
|
||||
RoleID string
|
||||
}
|
||||
addedRoles := make(map[saRoleKey]struct{})
|
||||
|
||||
now := time.Now()
|
||||
|
||||
for _, oldKey := range oldKeys {
|
||||
user, ok := userMap[oldKey.UserID]
|
||||
if !ok {
|
||||
// User not found — skip this key.
|
||||
// this should never happen as a key cannot exist without a user
|
||||
continue
|
||||
}
|
||||
|
||||
// Create service account for this user if not already created.
|
||||
saID, exists := userIDToServiceAccountID[oldKey.UserID]
|
||||
if !exists {
|
||||
saID = valuer.GenerateUUID().String()
|
||||
userIDToServiceAccountID[oldKey.UserID] = saID
|
||||
saID := valuer.GenerateUUID()
|
||||
serviceAccounts = append(serviceAccounts, &newServiceAccount68{
|
||||
Identifiable: types.Identifiable{ID: saID},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Name: oldKey.Name,
|
||||
Email: user.Email,
|
||||
Status: "active",
|
||||
OrgID: user.OrgID,
|
||||
})
|
||||
|
||||
serviceAccounts = append(serviceAccounts, &newServiceAccount68{
|
||||
Identifiable: types.Identifiable{ID: valuer.MustNewUUID(saID)},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
Name: oldKey.Name,
|
||||
Email: user.Email,
|
||||
Status: "active",
|
||||
OrgID: user.OrgID,
|
||||
})
|
||||
}
|
||||
|
||||
// Map the old role (ADMIN, EDITOR, VIEWER) to the managed role name.
|
||||
managedRoleName, ok := roletypes.ExistingRoleToSigNozManagedRoleMap[types.Role(oldKey.Role)]
|
||||
if !ok {
|
||||
managedRoleName = roletypes.SigNozViewerRoleName
|
||||
@@ -221,20 +201,15 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
|
||||
roleID, ok := roleMap[orgRoleKey{OrgID: user.OrgID, RoleName: managedRoleName}]
|
||||
if ok {
|
||||
key := saRoleKey{ServiceAccountID: saID, RoleID: roleID}
|
||||
if _, added := addedRoles[key]; !added {
|
||||
addedRoles[key] = struct{}{}
|
||||
serviceAccountRoles = append(serviceAccountRoles, &newServiceAccountRole68{
|
||||
Identifiable: types.Identifiable{ID: valuer.GenerateUUID()},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ServiceAccountID: saID,
|
||||
RoleID: roleID,
|
||||
})
|
||||
}
|
||||
serviceAccountRoles = append(serviceAccountRoles, &newServiceAccountRole68{
|
||||
Identifiable: types.Identifiable{ID: valuer.GenerateUUID()},
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
ServiceAccountID: saID.String(),
|
||||
RoleID: roleID,
|
||||
})
|
||||
}
|
||||
|
||||
// Convert expires_at from time.Time to unix seconds (0 = never expires).
|
||||
var expiresAtUnix uint64
|
||||
if !oldKey.ExpiresAt.IsZero() && oldKey.ExpiresAt.Unix() > 0 {
|
||||
expiresAtUnix = uint64(oldKey.ExpiresAt.Unix())
|
||||
@@ -254,11 +229,10 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
Key: oldKey.Token,
|
||||
ExpiresAt: expiresAtUnix,
|
||||
LastObservedAt: lastObservedAt,
|
||||
ServiceAccountID: saID,
|
||||
ServiceAccountID: saID.String(),
|
||||
})
|
||||
}
|
||||
|
||||
// Step 6: Insert migrated service accounts and roles.
|
||||
if len(serviceAccounts) > 0 {
|
||||
if _, err := tx.NewInsert().Model(&serviceAccounts).Exec(ctx); err != nil {
|
||||
return err
|
||||
@@ -271,9 +245,7 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 7: Drop old table, create new table, and insert migrated keys.
|
||||
sqls := [][]byte{}
|
||||
|
||||
deprecatedFactorAPIKey, _, err := migration.sqlschema.GetTable(ctx, sqlschema.TableName("factor_api_key"))
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -319,7 +291,6 @@ func (migration *deprecateAPIKey) Up(ctx context.Context, db *bun.DB) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Step 8: Insert migrated keys into the new table.
|
||||
if len(newKeys) > 0 {
|
||||
if _, err := tx.NewInsert().Model(&newKeys).Exec(ctx); err != nil {
|
||||
return err
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
var NEVER_EXPIRES = time.Unix(0, 0)
|
||||
|
||||
type PostableAPIKey struct {
|
||||
Name string `json:"name"`
|
||||
Role Role `json:"role"`
|
||||
ExpiresInDays int64 `json:"expiresInDays"`
|
||||
}
|
||||
|
||||
type GettableAPIKey struct {
|
||||
Identifiable
|
||||
TimeAuditable
|
||||
UserAuditable
|
||||
Token string `json:"token"`
|
||||
Role Role `json:"role"`
|
||||
Name string `json:"name"`
|
||||
ExpiresAt int64 `json:"expiresAt"`
|
||||
LastUsed int64 `json:"lastUsed"`
|
||||
Revoked bool `json:"revoked"`
|
||||
UserID string `json:"userId"`
|
||||
CreatedByUser *User `json:"createdByUser"`
|
||||
UpdatedByUser *User `json:"updatedByUser"`
|
||||
}
|
||||
|
||||
type OrgUserAPIKey struct {
|
||||
*Organization `bun:",extend"`
|
||||
Users []*UserWithAPIKey `bun:"rel:has-many,join:id=org_id"`
|
||||
}
|
||||
|
||||
type UserWithAPIKey struct {
|
||||
*User `bun:",extend"`
|
||||
APIKeys []*StorableAPIKeyUser `bun:"rel:has-many,join:id=user_id"`
|
||||
}
|
||||
|
||||
type StorableAPIKeyUser struct {
|
||||
StorableAPIKey `bun:",extend"`
|
||||
|
||||
CreatedByUser *User `json:"createdByUser" bun:"created_by_user,rel:belongs-to,join:created_by=id"`
|
||||
UpdatedByUser *User `json:"updatedByUser" bun:"updated_by_user,rel:belongs-to,join:updated_by=id"`
|
||||
}
|
||||
|
||||
type StorableAPIKey struct {
|
||||
bun.BaseModel `bun:"table:factor_api_key"`
|
||||
|
||||
Identifiable
|
||||
TimeAuditable
|
||||
UserAuditable
|
||||
Token string `json:"token" bun:"token,type:text,notnull,unique"`
|
||||
Role Role `json:"role" bun:"role,type:text,notnull,default:'ADMIN'"`
|
||||
Name string `json:"name" bun:"name,type:text,notnull"`
|
||||
ExpiresAt time.Time `json:"-" bun:"expires_at,notnull,nullzero,type:timestamptz"`
|
||||
LastUsed time.Time `json:"-" bun:"last_used,notnull,nullzero,type:timestamptz"`
|
||||
Revoked bool `json:"revoked" bun:"revoked,notnull,default:false"`
|
||||
UserID valuer.UUID `json:"userId" bun:"user_id,type:text,notnull"`
|
||||
}
|
||||
|
||||
func NewStorableAPIKey(name string, userID valuer.UUID, role Role, expiresAt int64) (*StorableAPIKey, error) {
|
||||
// validate
|
||||
|
||||
// we allow the APIKey if expiresAt is not set, which means it never expires
|
||||
if expiresAt < 0 {
|
||||
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "expiresAt must be greater than 0")
|
||||
}
|
||||
|
||||
if name == "" {
|
||||
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "name cannot be empty")
|
||||
}
|
||||
|
||||
if role == "" {
|
||||
return nil, errors.New(errors.TypeInvalidInput, errors.CodeInvalidInput, "role cannot be empty")
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
// convert expiresAt to unix timestamp from days
|
||||
// expiresAt = now.Unix() + (expiresAt * 24 * 60 * 60)
|
||||
expiresAtTime := now.AddDate(0, 0, int(expiresAt))
|
||||
|
||||
// if the expiresAt is 0, it means the APIKey never expires
|
||||
if expiresAt == 0 {
|
||||
expiresAtTime = NEVER_EXPIRES
|
||||
}
|
||||
|
||||
// Generate a 32-byte random token.
|
||||
token := make([]byte, 32)
|
||||
_, err := rand.Read(token)
|
||||
if err != nil {
|
||||
return nil, errors.New(errors.TypeInternal, errors.CodeInternal, "failed to generate token")
|
||||
}
|
||||
// Encode the token in base64.
|
||||
encodedToken := base64.StdEncoding.EncodeToString(token)
|
||||
|
||||
return &StorableAPIKey{
|
||||
Identifiable: Identifiable{
|
||||
ID: valuer.GenerateUUID(),
|
||||
},
|
||||
TimeAuditable: TimeAuditable{
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
},
|
||||
UserAuditable: UserAuditable{
|
||||
CreatedBy: userID.String(),
|
||||
UpdatedBy: userID.String(),
|
||||
},
|
||||
Token: encodedToken,
|
||||
Name: name,
|
||||
Role: role,
|
||||
UserID: userID,
|
||||
ExpiresAt: expiresAtTime,
|
||||
LastUsed: now,
|
||||
Revoked: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewGettableAPIKeyFromStorableAPIKey(storableAPIKey *StorableAPIKeyUser) *GettableAPIKey {
|
||||
lastUsed := storableAPIKey.LastUsed.Unix()
|
||||
if storableAPIKey.LastUsed == storableAPIKey.CreatedAt {
|
||||
lastUsed = 0
|
||||
}
|
||||
return &GettableAPIKey{
|
||||
Identifiable: storableAPIKey.Identifiable,
|
||||
TimeAuditable: storableAPIKey.TimeAuditable,
|
||||
UserAuditable: storableAPIKey.UserAuditable,
|
||||
Token: storableAPIKey.Token,
|
||||
Role: storableAPIKey.Role,
|
||||
Name: storableAPIKey.Name,
|
||||
ExpiresAt: storableAPIKey.ExpiresAt.Unix(),
|
||||
LastUsed: lastUsed,
|
||||
Revoked: storableAPIKey.Revoked,
|
||||
UserID: storableAPIKey.UserID.String(),
|
||||
CreatedByUser: storableAPIKey.CreatedByUser,
|
||||
UpdatedByUser: storableAPIKey.UpdatedByUser,
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ type Store interface {
|
||||
Get(context.Context, valuer.UUID, valuer.UUID) (*StorableServiceAccount, error)
|
||||
GetActiveByOrgIDAndName(context.Context, valuer.UUID, string) (*StorableServiceAccount, error)
|
||||
GetByID(context.Context, valuer.UUID) (*StorableServiceAccount, error)
|
||||
GetByOrgIDAndName(context.Context, valuer.UUID, string) (*StorableServiceAccount, error)
|
||||
List(context.Context, valuer.UUID) ([]*StorableServiceAccount, error)
|
||||
Update(context.Context, valuer.UUID, *StorableServiceAccount) error
|
||||
Delete(context.Context, valuer.UUID, valuer.UUID) error
|
||||
|
||||
@@ -255,14 +255,6 @@ type UserStore interface {
|
||||
DeleteResetPasswordTokenByPasswordID(ctx context.Context, passwordID valuer.UUID) error
|
||||
UpdatePassword(ctx context.Context, password *FactorPassword) error
|
||||
|
||||
// API KEY
|
||||
CreateAPIKey(ctx context.Context, apiKey *StorableAPIKey) error
|
||||
UpdateAPIKey(ctx context.Context, id valuer.UUID, apiKey *StorableAPIKey, updaterID valuer.UUID) error
|
||||
ListAPIKeys(ctx context.Context, orgID valuer.UUID) ([]*StorableAPIKeyUser, error)
|
||||
RevokeAPIKey(ctx context.Context, id valuer.UUID, revokedByUserID valuer.UUID) error
|
||||
GetAPIKey(ctx context.Context, orgID, id valuer.UUID) (*StorableAPIKeyUser, error)
|
||||
CountAPIKeyByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error)
|
||||
|
||||
CountByOrgID(ctx context.Context, orgID valuer.UUID) (int64, error)
|
||||
CountByOrgIDAndStatuses(ctx context.Context, orgID valuer.UUID, statuses []string) (map[valuer.String]int64, error)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user