1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-04 11:36:46 +00:00

feat: add static api key env variable (#1229)

This commit is contained in:
Elias Schneider
2026-01-11 15:36:27 +01:00
committed by GitHub
parent 9ca3d33c88
commit 746aa71d67
6 changed files with 78 additions and 6 deletions

View File

@@ -75,7 +75,12 @@ func initServices(ctx context.Context, db *gorm.DB, httpClient *http.Client, ima
svc.userGroupService = service.NewUserGroupService(db, svc.appConfigService, svc.scimService) svc.userGroupService = service.NewUserGroupService(db, svc.appConfigService, svc.scimService)
svc.userService = service.NewUserService(db, svc.jwtService, svc.auditLogService, svc.emailService, svc.appConfigService, svc.customClaimService, svc.appImagesService, svc.scimService, fileStorage) svc.userService = service.NewUserService(db, svc.jwtService, svc.auditLogService, svc.emailService, svc.appConfigService, svc.customClaimService, svc.appImagesService, svc.scimService, fileStorage)
svc.ldapService = service.NewLdapService(db, httpClient, svc.appConfigService, svc.userService, svc.userGroupService, fileStorage) svc.ldapService = service.NewLdapService(db, httpClient, svc.appConfigService, svc.userService, svc.userGroupService, fileStorage)
svc.apiKeyService = service.NewApiKeyService(db, svc.emailService)
svc.apiKeyService, err = service.NewApiKeyService(ctx, db, svc.emailService)
if err != nil {
return nil, fmt.Errorf("failed to create API key service: %w", err)
}
svc.userSignUpService = service.NewUserSignupService(db, svc.jwtService, svc.auditLogService, svc.appConfigService, svc.userService) svc.userSignUpService = service.NewUserSignupService(db, svc.jwtService, svc.auditLogService, svc.appConfigService, svc.userService)
svc.oneTimeAccessService = service.NewOneTimeAccessService(db, svc.userService, svc.jwtService, svc.auditLogService, svc.emailService, svc.appConfigService) svc.oneTimeAccessService = service.NewOneTimeAccessService(db, svc.userService, svc.jwtService, svc.auditLogService, svc.emailService, svc.appConfigService)

View File

@@ -50,6 +50,7 @@ type EnvConfigSchema struct {
InternalAppURL string `env:"INTERNAL_APP_URL"` InternalAppURL string `env:"INTERNAL_APP_URL"`
UiConfigDisabled bool `env:"UI_CONFIG_DISABLED"` UiConfigDisabled bool `env:"UI_CONFIG_DISABLED"`
DisableRateLimiting bool `env:"DISABLE_RATE_LIMITING"` DisableRateLimiting bool `env:"DISABLE_RATE_LIMITING"`
StaticApiKey string `env:"STATIC_API_KEY" options:"file"`
FileBackend string `env:"FILE_BACKEND" options:"toLower"` FileBackend string `env:"FILE_BACKEND" options:"toLower"`
UploadPath string `env:"UPLOAD_PATH"` UploadPath string `env:"UPLOAD_PATH"`
@@ -200,6 +201,10 @@ func ValidateEnvConfig(config *EnvConfigSchema) error {
return errors.New("AUDIT_LOG_RETENTION_DAYS must be greater than 0") return errors.New("AUDIT_LOG_RETENTION_DAYS must be greater than 0")
} }
if config.StaticApiKey != "" && len(config.StaticApiKey) < 16 {
return errors.New("STATIC_API_KEY must be at least 16 characters long")
}
return nil return nil
} }

View File

@@ -34,7 +34,7 @@ func (m *ApiKeyAuthMiddleware) Add(adminRequired bool) gin.HandlerFunc {
} }
func (m *ApiKeyAuthMiddleware) Verify(c *gin.Context, adminRequired bool) (userID string, isAdmin bool, err error) { func (m *ApiKeyAuthMiddleware) Verify(c *gin.Context, adminRequired bool) (userID string, isAdmin bool, err error) {
apiKey := c.GetHeader("X-API-KEY") apiKey := c.GetHeader("X-API-Key")
user, err := m.apiKeyService.ValidateApiKey(c.Request.Context(), apiKey) user, err := m.apiKeyService.ValidateApiKey(c.Request.Context(), apiKey)
if err != nil { if err != nil {

View File

@@ -16,13 +16,25 @@ import (
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
) )
const staticApiKeyUserID = "00000000-0000-0000-0000-000000000000"
type ApiKeyService struct { type ApiKeyService struct {
db *gorm.DB db *gorm.DB
emailService *EmailService emailService *EmailService
} }
func NewApiKeyService(db *gorm.DB, emailService *EmailService) *ApiKeyService { func NewApiKeyService(ctx context.Context, db *gorm.DB, emailService *EmailService) (*ApiKeyService, error) {
return &ApiKeyService{db: db, emailService: emailService} s := &ApiKeyService{db: db, emailService: emailService}
if common.EnvConfig.StaticApiKey == "" {
err := s.deleteStaticApiKeyUser(ctx)
if err != nil {
return nil, err
}
}
return s, nil
} }
func (s *ApiKeyService) ListApiKeys(ctx context.Context, userID string, listRequestOptions utils.ListRequestOptions) ([]model.ApiKey, utils.PaginationResponse, error) { func (s *ApiKeyService) ListApiKeys(ctx context.Context, userID string, listRequestOptions utils.ListRequestOptions) ([]model.ApiKey, utils.PaginationResponse, error) {
@@ -144,6 +156,10 @@ func (s *ApiKeyService) ValidateApiKey(ctx context.Context, apiKey string) (mode
return model.User{}, &common.NoAPIKeyProvidedError{} return model.User{}, &common.NoAPIKeyProvidedError{}
} }
if common.EnvConfig.StaticApiKey != "" && apiKey == common.EnvConfig.StaticApiKey {
return s.initStaticApiKeyUser(ctx)
}
now := time.Now() now := time.Now()
hashedKey := utils.CreateSha256Hash(apiKey) hashedKey := utils.CreateSha256Hash(apiKey)
@@ -217,3 +233,47 @@ func (s *ApiKeyService) SendApiKeyExpiringSoonEmail(ctx context.Context, apiKey
Update("expiration_email_sent", true). Update("expiration_email_sent", true).
Error Error
} }
func (s *ApiKeyService) initStaticApiKeyUser(ctx context.Context) (user model.User, err error) {
err = s.db.
WithContext(ctx).
First(&user, "id = ?", staticApiKeyUserID).
Error
if err == nil {
return user, nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
return model.User{}, err
}
usernameSuffix, err := utils.GenerateRandomAlphanumericString(6)
if err != nil {
return model.User{}, err
}
user = model.User{
Base: model.Base{
ID: staticApiKeyUserID,
},
FirstName: "Static API User",
Username: "static-api-user-" + usernameSuffix,
DisplayName: "Static API User",
IsAdmin: true,
}
err = s.db.
WithContext(ctx).
Create(&user).
Error
return user, err
}
func (s *ApiKeyService) deleteStaticApiKeyUser(ctx context.Context) error {
return s.db.
WithContext(ctx).
Delete(&model.User{}, "id = ?", staticApiKeyUserID).
Error
}

View File

@@ -125,7 +125,9 @@ func (s *UserSignUpService) SignUpInitialAdmin(ctx context.Context, signUpData d
}() }()
var userCount int64 var userCount int64
if err := tx.WithContext(ctx).Model(&model.User{}).Count(&userCount).Error; err != nil { if err := tx.WithContext(ctx).Model(&model.User{}).
Where("id != ?", staticApiKeyUserID).
Count(&userCount).Error; err != nil {
return model.User{}, "", err return model.User{}, "", err
} }
if userCount != 0 { if userCount != 0 {

View File

@@ -48,10 +48,10 @@
try { try {
await userService.remove(user.id); await userService.remove(user.id);
await refresh(); await refresh();
toast.success(m.user_deleted_successfully());
} catch (e) { } catch (e) {
axiosErrorToast(e); axiosErrorToast(e);
} }
toast.success(m.user_deleted_successfully());
} }
} }
}); });