mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-02-04 15:04:43 +00:00
feat: add static api key env variable (#1229)
This commit is contained in:
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user