Skip to content

Commit

Permalink
Merge pull request #4 from teamhanko/feat/multi-tenancy
Browse files Browse the repository at this point in the history
feat(tenancy): introduce multi tenancy
  • Loading branch information
FreddyDevelop authored Nov 8, 2023
2 parents 2b7d315 + 6bf0825 commit b6a3381
Show file tree
Hide file tree
Showing 131 changed files with 4,125 additions and 1,019 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
*.so
*.dylib



#env files
*.env

Expand All @@ -24,4 +26,4 @@
*.iml

# version
/backend/build_info/version.txt
server/build_info/version.txt
5 changes: 3 additions & 2 deletions deploy/docker-compose/backend.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@ services:
- type: bind
source: ./config.yaml
target: /etc/config/config.yaml
command: --config /etc/config/config.yaml serve
command: --config /etc/config/config.yaml serve all
ports:
- '8000:8000'
- '8001:8001'
restart: unless-stopped
depends_on:
passkey-migrate:
Expand All @@ -37,7 +38,7 @@ services:
- POSTGRES_PASSWORD=hanko
- POSTGRES_DB=passkey
healthcheck:
test: pg_isready -U hanko -d hanko
test: pg_isready -U hanko -d passkey
interval: 10s
timeout: 10s
retries: 3
Expand Down
5 changes: 0 additions & 5 deletions deploy/docker-compose/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,3 @@ database:
port: 5432
user: hanko
password: hanko
secrets:
api_keys:
- 2d92ff02-a646-4ddb-948a-931f122b4371
keys:
- super-long-and-super-secret
16 changes: 12 additions & 4 deletions server/api/api.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package api

import (
router2 "github.com/teamhanko/passkey-server/api/router"
"github.com/labstack/echo/v4"
"github.com/teamhanko/passkey-server/api/router"
"github.com/teamhanko/passkey-server/config"
"github.com/teamhanko/passkey-server/persistence"
"sync"
)

func Start(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister) {
func StartPublic(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister) {
defer wg.Done()

router := router2.NewMainRouter(cfg, persister)
router.Logger.Fatal(router.Start(cfg.Server.Address))
mainRouter := router.NewMainRouter(cfg, persister)
mainRouter.Logger.Fatal(mainRouter.Start(cfg.Address))
}

func StartAdmin(cfg *config.Config, wg *sync.WaitGroup, persister persistence.Persister, prometheus echo.MiddlewareFunc) {
defer wg.Done()

adminRouter := router.NewAdminRouter(cfg, persister, prometheus)
adminRouter.Logger.Fatal(adminRouter.Start(cfg.AdminAddress))
}
45 changes: 45 additions & 0 deletions server/api/dto/admin/request/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package request

import (
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/config"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateConfigDto struct {
Cors CreateCorsDto `json:"cors" validate:"required"`
Webauthn CreateWebauthnDto `json:"webauthn" validate:"required"`
}

func (dto *CreateConfigDto) ToModel(tenant models.Tenant) models.Config {
configId, _ := uuid.NewV4()
now := time.Now()

auditLogId, _ := uuid.NewV4()
auditLogModel := models.AuditLogConfig{
ID: auditLogId,
ConfigID: configId,
OutputStream: config.OutputStreamStdOut,
ConsoleEnabled: true,
StorageEnabled: true,
CreatedAt: now,
UpdatedAt: now,
}

configModel := models.Config{
ID: configId,
TenantID: tenant.ID,
AuditLogConfig: auditLogModel,
Secrets: nil,
CreatedAt: now,
UpdatedAt: now,
}

return configModel
}

type UpdateConfigDto struct {
GetTenantDto
CreateConfigDto
}
42 changes: 42 additions & 0 deletions server/api/dto/admin/request/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package request

import (
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateCorsDto struct {
AllowedOrigins []string `json:"allowed_origins" validate:"required,min=1"`
AllowUnsafeWildcard bool `json:"allow_unsafe_wildcard" validate:"required,boolean"`
}

func (dto *CreateCorsDto) ToModel(config models.Config) models.Cors {
corsId, _ := uuid.NewV4()
now := time.Now()

var origins models.CorsOrigins

for _, origin := range dto.AllowedOrigins {
originId, _ := uuid.NewV4()
originModel := models.CorsOrigin{
ID: originId,
Origin: origin,
CreatedAt: now,
UpdatedAt: now,
}

origins = append(origins, originModel)
}

cors := models.Cors{
ID: corsId,
ConfigID: config.ID,
AllowUnsafe: dto.AllowUnsafeWildcard,
Origins: origins,
CreatedAt: now,
UpdatedAt: now,
}

return cors
}
45 changes: 45 additions & 0 deletions server/api/dto/admin/request/relying_party.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package request

import (
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateRelyingPartyDto struct {
Id string `json:"id" validate:"required"`
DisplayName string `json:"display_name" validate:"required"`
Icon *string `json:"icon"`
Origins []string `json:"origins"`
}

func (dto *CreateRelyingPartyDto) ToModel(config models.WebauthnConfig) models.RelyingParty {
rpId, _ := uuid.NewV4()
now := time.Now()
var origins models.WebauthnOrigins

for _, origin := range dto.Origins {
originId, _ := uuid.NewV4()
originModel := models.WebauthnOrigin{
ID: originId,
Origin: origin,
CreatedAt: now,
UpdatedAt: now,
}

origins = append(origins, originModel)
}

relyingParty := models.RelyingParty{
ID: rpId,
WebauthnConfigID: config.ID,
RPId: dto.Id,
DisplayName: dto.DisplayName,
Icon: dto.Icon,
Origins: origins,
CreatedAt: now,
UpdatedAt: now,
}

return relyingParty
}
39 changes: 39 additions & 0 deletions server/api/dto/admin/request/secrets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package request

import (
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/crypto"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateSecretDto struct {
GetTenantDto
Name string `json:"name"`
}

func (dto *CreateSecretDto) ToModel(config *models.Config, isApiKey bool) (*models.Secret, error) {
secretId, _ := uuid.NewV4()

secretKey, err := crypto.GenerateRandomStringURLSafe(64)
if err != nil {
return nil, err
}

now := time.Now()

return &models.Secret{
ID: secretId,
Name: dto.Name,
Key: secretKey,
IsAPISecret: isApiKey,
Config: config,
CreatedAt: now,
UpdatedAt: now,
}, nil
}

type RemoveSecretDto struct {
GetTenantDto
SecretId string `param:"secret_id" validate:"required,uuid4"`
}
35 changes: 35 additions & 0 deletions server/api/dto/admin/request/tenant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package request

import (
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateTenantDto struct {
DisplayName string `json:"display_name" validate:"required"`
Config CreateConfigDto `json:"config" validate:"required"`
}

func (dto *CreateTenantDto) ToModel() models.Tenant {
tenantId, _ := uuid.NewV4()
now := time.Now()

tenant := models.Tenant{
ID: tenantId,
DisplayName: dto.DisplayName,
CreatedAt: now,
UpdatedAt: now,
}

return tenant
}

type GetTenantDto struct {
Id string `param:"tenant_id" validate:"required,uuid4"`
}

type UpdateTenantDto struct {
GetTenantDto
DisplayName string `json:"display_name" validate:"required"`
}
30 changes: 30 additions & 0 deletions server/api/dto/admin/request/webauthn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package request

import (
"github.com/go-webauthn/webauthn/protocol"
"github.com/gofrs/uuid"
"github.com/teamhanko/passkey-server/persistence/models"
"time"
)

type CreateWebauthnDto struct {
RelyingParty CreateRelyingPartyDto `json:"relying_party" validate:"required"`
Timeout int `json:"timeout" validate:"required,number"`
UserVerification protocol.UserVerificationRequirement `json:"user_verification" validate:"required,oneof=required preferred discouraged"`
}

func (dto *CreateWebauthnDto) ToModel(configModel models.Config) models.WebauthnConfig {
webauthnConfigId, _ := uuid.NewV4()
now := time.Now()

webauthnConfig := models.WebauthnConfig{
ID: webauthnConfigId,
ConfigID: configModel.ID,
Timeout: dto.Timeout,
CreatedAt: now,
UpdatedAt: now,
UserVerification: dto.UserVerification,
}

return webauthnConfig
}
15 changes: 15 additions & 0 deletions server/api/dto/admin/response/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package response

import "github.com/teamhanko/passkey-server/persistence/models"

type GetConfigResponse struct {
Cors GetCorsResponse `json:"cors"`
Webauthn GetWebauthnResponse `json:"webauthn"`
}

func ToGetConfigResponse(config *models.Config) GetConfigResponse {
return GetConfigResponse{
Cors: ToGetCorsResponse(&config.Cors),
Webauthn: ToGetWebauthnResponse(&config.WebauthnConfig),
}
}
20 changes: 20 additions & 0 deletions server/api/dto/admin/response/cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package response

import "github.com/teamhanko/passkey-server/persistence/models"

type GetCorsResponse struct {
AllowedOrigins []string `json:"allowed_origins"`
AllowUnsafe bool `json:"allow_unsafe_wildcard"`
}

func ToGetCorsResponse(cors *models.Cors) GetCorsResponse {
var origins []string
for _, origin := range cors.Origins {
origins = append(origins, origin.Origin)
}

return GetCorsResponse{
AllowedOrigins: origins,
AllowUnsafe: cors.AllowUnsafe,
}
}
24 changes: 24 additions & 0 deletions server/api/dto/admin/response/relying_party.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package response

import "github.com/teamhanko/passkey-server/persistence/models"

type GetRelyingPartyResponse struct {
Id string `json:"id"`
DisplayName string `json:"display_name"`
Icon *string `json:"icon,omitempty"`
Origins []string `json:"origins"`
}

func ToGetRelyingPartyResponse(relyingParty *models.RelyingParty) GetRelyingPartyResponse {
var origins []string
for _, origin := range relyingParty.Origins {
origins = append(origins, origin.Origin)
}

return GetRelyingPartyResponse{
Id: relyingParty.RPId,
DisplayName: relyingParty.DisplayName,
Icon: relyingParty.Icon,
Origins: origins,
}
}
Loading

0 comments on commit b6a3381

Please sign in to comment.