Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/gin-contrib/pprof v1.5.3
github.com/gin-gonic/gin v1.11.0
github.com/go-resty/resty/v2 v2.17.1
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/google/uuid v1.6.0
github.com/gorilla/websocket v1.5.3
github.com/hashicorp/vault/api v1.22.0
Expand Down Expand Up @@ -63,6 +64,7 @@ require (
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beevik/etree v1.5.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bytedance/gopkg v0.1.3 // indirect
Expand All @@ -83,6 +85,7 @@ require (
github.com/coreos/go-semver v0.3.1 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/crewjam/saml v0.5.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
Expand Down Expand Up @@ -145,6 +148,7 @@ require (
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.0 // indirect
Expand All @@ -153,6 +157,7 @@ require (
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
Expand Down Expand Up @@ -187,6 +192,7 @@ require (
github.com/prometheus/procfs v0.19.2 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/russellhaering/goxmldsig v1.4.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
Expand Down
15 changes: 15 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:W
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/beevik/etree v1.5.0 h1:iaQZFSDS+3kYZiGoc9uKeOkUY3nYMXOKLl6KIJxiJWs=
github.com/beevik/etree v1.5.0/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
Expand Down Expand Up @@ -277,6 +280,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/crewjam/saml v0.5.1 h1:g+mfp0CrLuLRZCK793PgJcZeg5dS/0CDwoeAX2zcwNI=
github.com/crewjam/saml v0.5.1/go.mod h1:r0fDkmFe5URDgPrmtH0IYokva6fac3AUdstiPhyEolQ=
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
Expand Down Expand Up @@ -503,6 +508,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=
github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
Expand Down Expand Up @@ -695,6 +702,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
Expand All @@ -720,6 +728,8 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
Expand Down Expand Up @@ -857,6 +867,7 @@ github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrap
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
Expand Down Expand Up @@ -916,8 +927,12 @@ github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6So
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys=
github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
Expand Down
8 changes: 7 additions & 1 deletion internal/api/setup/dependencies.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type Repositories struct {
RouteTable ports.RouteTableRepository
IGW ports.IGWRepository
NATGateway ports.NATGatewayRepository
IdP ports.IdentityProviderRepository
FederatedIdentity ports.FederatedIdentityRepository
}

// InitRepositories constructs repositories using the provided database clients.
Expand Down Expand Up @@ -127,6 +129,8 @@ func InitRepositories(db postgres.DB, rdb *redisv9.Client) *Repositories {
RouteTable: postgres.NewRouteTableRepository(db),
IGW: postgres.NewIGWRepository(db),
NATGateway: postgres.NewNATGatewayRepository(db),
IdP: postgres.NewIdentityProviderRepository(db),
FederatedIdentity: postgres.NewFederatedIdentityRepository(db),
}
}

Expand Down Expand Up @@ -178,6 +182,8 @@ type Services struct {
RouteTable *services.RouteTableService
InternetGateway *services.InternetGatewayService
NATGateway *services.NATGatewayService
IdP *services.IdentityProviderService
JWT *services.JWTService
}

// Shutdown cleanly stops all services.
Expand Down Expand Up @@ -349,7 +355,7 @@ func InitServices(c ServiceConfig) (*Services, *Workers, error) {
return nil, nil, err
}

svcs := &Services{WsHub: wsHub, Audit: auditSvc, Identity: identitySvc, Tenant: tenantSvc, Auth: authSvc, PasswordReset: pwdResetSvc, RBAC: rbacSvc, Vpc: vpcSvc, Subnet: subnetSvc, Event: eventSvc, Volume: volumeSvc, Instance: instSvcConcrete, SecurityGroup: sgSvc, LB: lbSvc, Snapshot: snapshotSvc, Stack: stackSvc, Storage: storageSvc, Database: databaseSvc, Secret: secretSvc, Function: fnSvc, FunctionSchedule: fnSchedSvc, Cache: cacheSvc, Queue: queueSvc, Notify: notifySvc, Cron: cronSvc, Gateway: gwSvc, Container: containerSvc, Pipeline: pipelineSvc, Health: services.NewHealthServiceImpl(c.DB, c.Compute, clusterSvc), AutoScaling: asgSvc, Accounting: accountingSvc, Image: imageSvc, Cluster: clusterSvc, Dashboard: services.NewDashboardService(rbacSvc, c.Repos.Instance, c.Repos.Volume, c.Repos.Vpc, c.Repos.Event, c.Logger), Lifecycle: services.NewLifecycleService(c.Repos.Lifecycle, rbacSvc, c.Repos.Storage), InstanceType: services.NewInstanceTypeService(c.Repos.InstanceType, rbacSvc), GlobalLB: glbSvc, DNS: dnsSvc, SSHKey: sshKeySvc, ElasticIP: services.NewElasticIPService(services.ElasticIPServiceParams{Repo: c.Repos.ElasticIP, RBAC: rbacSvc, InstanceRepo: c.Repos.Instance, AuditSvc: auditSvc, Logger: c.Logger}), Log: logSvc, IAM: iamSvc, VPCPeering: services.NewVPCPeeringService(services.VPCPeeringServiceParams{Repo: c.Repos.VPCPeering, VpcRepo: c.Repos.Vpc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger}), RouteTable: services.NewRouteTableService(services.RouteTableServiceParams{Repo: c.Repos.RouteTable, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger}), InternetGateway: services.NewInternetGatewayService(services.InternetGatewayServiceParams{Repo: c.Repos.IGW, RTRepo: c.Repos.RouteTable, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, AuditSvc: auditSvc, Logger: c.Logger}), NATGateway: services.NewNATGatewayService(services.NATGatewayServiceParams{Repo: c.Repos.NATGateway, EIPRepo: c.Repos.ElasticIP, SubnetRepo: c.Repos.Subnet, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger})}
svcs := &Services{WsHub: wsHub, Audit: auditSvc, Identity: identitySvc, Tenant: tenantSvc, Auth: authSvc, PasswordReset: pwdResetSvc, RBAC: rbacSvc, Vpc: vpcSvc, Subnet: subnetSvc, Event: eventSvc, Volume: volumeSvc, Instance: instSvcConcrete, SecurityGroup: sgSvc, LB: lbSvc, Snapshot: snapshotSvc, Stack: stackSvc, Storage: storageSvc, Database: databaseSvc, Secret: secretSvc, Function: fnSvc, FunctionSchedule: fnSchedSvc, Cache: cacheSvc, Queue: queueSvc, Notify: notifySvc, Cron: cronSvc, Gateway: gwSvc, Container: containerSvc, Pipeline: pipelineSvc, Health: services.NewHealthServiceImpl(c.DB, c.Compute, clusterSvc), AutoScaling: asgSvc, Accounting: accountingSvc, Image: imageSvc, Cluster: clusterSvc, Dashboard: services.NewDashboardService(rbacSvc, c.Repos.Instance, c.Repos.Volume, c.Repos.Vpc, c.Repos.Event, c.Logger), Lifecycle: services.NewLifecycleService(c.Repos.Lifecycle, rbacSvc, c.Repos.Storage), InstanceType: services.NewInstanceTypeService(c.Repos.InstanceType, rbacSvc), GlobalLB: glbSvc, DNS: dnsSvc, SSHKey: sshKeySvc, ElasticIP: services.NewElasticIPService(services.ElasticIPServiceParams{Repo: c.Repos.ElasticIP, RBAC: rbacSvc, InstanceRepo: c.Repos.Instance, AuditSvc: auditSvc, Logger: c.Logger}), Log: logSvc, IAM: iamSvc, VPCPeering: services.NewVPCPeeringService(services.VPCPeeringServiceParams{Repo: c.Repos.VPCPeering, VpcRepo: c.Repos.Vpc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger}), RouteTable: services.NewRouteTableService(services.RouteTableServiceParams{Repo: c.Repos.RouteTable, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger}), InternetGateway: services.NewInternetGatewayService(services.InternetGatewayServiceParams{Repo: c.Repos.IGW, RTRepo: c.Repos.RouteTable, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, AuditSvc: auditSvc, Logger: c.Logger}), NATGateway: services.NewNATGatewayService(services.NATGatewayServiceParams{Repo: c.Repos.NATGateway, EIPRepo: c.Repos.ElasticIP, SubnetRepo: c.Repos.Subnet, VpcRepo: c.Repos.Vpc, RBACSvc: rbacSvc, Network: c.Network, AuditSvc: auditSvc, Logger: c.Logger}), IdP: services.NewIdentityProviderService(services.IdentityProviderServiceParams{IdPRepo: c.Repos.IdP, FedIdentRepo: c.Repos.FederatedIdentity, UserRepo: c.Repos.User, TenantSvc: tenantSvc, TenantRepo: c.Repos.Tenant, AuditSvc: auditSvc, APIKeySvc: identitySvc, Logger: c.Logger}), JWT: services.NewJWTService(c.Config.SecretsEncryptionKey)}

// 7. High Availability & Monitoring
replicaMonitor := initReplicaMonitor(c)
Expand Down
37 changes: 34 additions & 3 deletions internal/api/setup/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ type Handlers struct {
VPCPeering *httphandlers.VPCPeeringHandler
RouteTable *httphandlers.RouteTableHandler
InternetGateway *httphandlers.InternetGatewayHandler
NATGateway *httphandlers.NATGatewayHandler
Ws *ws.Handler
Admin *httphandlers.AdminHandler
NATGateway *httphandlers.NATGatewayHandler
Ws *ws.Handler
Admin *httphandlers.AdminHandler
IdP *httphandlers.IdentityProviderHandler
}

// InitHandlers constructs HTTP handlers and websocket hub.
Expand Down Expand Up @@ -129,6 +130,7 @@ func InitHandlers(svcs *Services, cfg *platform.Config, logger *slog.Logger) *Ha
InternetGateway: httphandlers.NewInternetGatewayHandler(svcs.InternetGateway),
NATGateway: httphandlers.NewNATGatewayHandler(svcs.NATGateway),
Ws: ws.NewHandler(svcs.WsHub, svcs.Identity, logger, cfg.WSAllowedOrigins),
IdP: httphandlers.NewIdentityProviderHandler(svcs.IdP, svcs.Tenant),
}
}

Expand Down Expand Up @@ -263,6 +265,26 @@ func registerAuthRoutes(r *gin.Engine, handlers *Handlers, svcs *Services, cfg *
}

r.GET("/auth/me", httputil.Auth(svcs.Identity, svcs.Tenant), handlers.Auth.Me)

// SSO Routes (public - no auth required)
ssoGroup := r.Group("/auth/sso")
{
ssoGroup.GET("/oidc/:idp_id", handlers.IdP.InitiateOIDCLogin)
ssoGroup.GET("/oidc/:idp_id/callback", handlers.IdP.OIDCCallback)
ssoGroup.GET("/saml/:idp_id", handlers.IdP.InitiateSAMLLogin)
ssoGroup.POST("/saml/:idp_id/acs", handlers.IdP.SAMLACS)
}

// Admin IdP Management (requires auth + admin permissions)
idpGroup := r.Group("/admin/identity-providers")
idpGroup.Use(httputil.Auth(svcs.Identity, svcs.Tenant), httputil.Permission(svcs.RBAC, domain.PermissionFullAccess))
{
idpGroup.POST("", handlers.IdP.Create)
idpGroup.GET("", handlers.IdP.List)
idpGroup.GET("/:id", handlers.IdP.Get)
idpGroup.PUT("/:id", handlers.IdP.Update)
idpGroup.DELETE("/:id", handlers.IdP.Delete)
}
}

func registerComputeRoutes(r *gin.Engine, handlers *Handlers, svcs *Services) {
Expand Down Expand Up @@ -742,5 +764,14 @@ func registerTenantRoutes(r *gin.Engine, handlers *Handlers, svcs *Services) {
tenantGroup.POST("", handlers.Tenant.Create)
tenantGroup.POST("/:id/members", httputil.RequireTenant(), httputil.TenantMember(svcs.Tenant), handlers.Tenant.InviteMember)
tenantGroup.POST("/:id/switch", handlers.Tenant.SwitchTenant)

// Tenant-scoped IdP management
tenantIdpGroup := r.Group("/:tenant_id/identity-providers")
tenantIdpGroup.Use(httputil.Auth(svcs.Identity, svcs.Tenant), httputil.RequireTenant(), httputil.TenantMember(svcs.Tenant))
{
tenantIdpGroup.POST("", httputil.Permission(svcs.RBAC, domain.PermissionFullAccess), handlers.IdP.Create)
tenantIdpGroup.GET("", handlers.IdP.List)
tenantIdpGroup.DELETE("/:id", httputil.Permission(svcs.RBAC, domain.PermissionFullAccess), handlers.IdP.Delete)
}
}
}
66 changes: 66 additions & 0 deletions internal/core/domain/identity_provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package domain

import (
"time"

"github.com/google/uuid"
)

// IdentityProviderType distinguishes between OIDC and SAML protocols.
type IdentityProviderType string

const (
IdPTypeOIDC IdentityProviderType = "oidc"
IdPTypeSAML IdentityProviderType = "saml"
)

// IdentityProviderScope defines whether an IdP is global or per-tenant.
type IdentityProviderScope string

const (
IdPScopeGlobal IdentityProviderScope = "global"
IdPScopeTenant IdentityProviderScope = "tenant"
)

// GroupMapping maps IdP groups/roles to TheCloud roles.
type GroupMapping struct {
IdPGroup string `json:"idp_group"`
CloudRole string `json:"cloud_role"`
}

// IdentityProvider represents a configured external identity provider.
type IdentityProvider struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Type IdentityProviderType `json:"type"`
Scope IdentityProviderScope `json:"scope"`
TenantID *uuid.UUID `json:"tenant_id,omitempty"`
ClientID string `json:"client_id,omitempty"`
ClientSecret string `json:"-"` // OIDC: encrypted, never serialized
IssuerURL string `json:"issuer_url,omitempty"`
DiscoveryURL string `json:"discovery_url,omitempty"`
EntityID string `json:"entity_id,omitempty"`
SSOURL string `json:"sso_url,omitempty"`
MetadataURL string `json:"metadata_url,omitempty"`
Certificate string `json:"certificate,omitempty"`
Scopes []string `json:"scopes"`
RedirectURIs []string `json:"redirect_uris"`
Enabled bool `json:"enabled"`
DefaultRole string `json:"default_role"`
GroupMapping []GroupMapping `json:"group_mapping"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}

// FederatedIdentity maps an external IdP subject to a local user account.
type FederatedIdentity struct {
ID uuid.UUID `json:"id"`
UserID uuid.UUID `json:"user_id"`
IdPID uuid.UUID `json:"idp_id"`
Subject string `json:"subject"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Groups []string `json:"groups"`
LastLoginAt time.Time `json:"last_login_at"`
CreatedAt time.Time `json:"created_at"`
}
Loading
Loading