diff --git a/cmd/api/src/api/registration/v2.go b/cmd/api/src/api/registration/v2.go index 4d347c0e85..41388b8d07 100644 --- a/cmd/api/src/api/registration/v2.go +++ b/cmd/api/src/api/registration/v2.go @@ -62,7 +62,7 @@ func registerV2Auth(cfg config.Configuration, db database.Database, permissions routerInst.DELETE(fmt.Sprintf("/api/v2/saml/providers/{%s}", api.URIPathVariableSAMLProviderID), managementResource.DeleteSAMLProvider).RequirePermissions(permissions.AuthManageProviders), // SSO - routerInst.POST("/api/v2/sso/providers/oidc", managementResource.CreateOIDCProvider).CheckFeatureFlag(db, appcfg.FeatureOIDCSupport).RequirePermissions(permissions.AuthManageProviders), + routerInst.POST("/api/v2/sso-providers/oidc", managementResource.CreateOIDCProvider).CheckFeatureFlag(db, appcfg.FeatureOIDCSupport).RequirePermissions(permissions.AuthManageProviders), // Permissions routerInst.GET("/api/v2/permissions", managementResource.ListPermissions).RequirePermissions(permissions.AuthManageSelf), diff --git a/cmd/api/src/api/tools/analysis_schedule.go b/cmd/api/src/api/tools/analysis_schedule.go index b19b234c6a..84f22dd63e 100644 --- a/cmd/api/src/api/tools/analysis_schedule.go +++ b/cmd/api/src/api/tools/analysis_schedule.go @@ -1,3 +1,19 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + package tools import ( diff --git a/cmd/api/src/api/tools/analysis_schedule_test.go b/cmd/api/src/api/tools/analysis_schedule_test.go index 51c272e9a5..9693cbb855 100644 --- a/cmd/api/src/api/tools/analysis_schedule_test.go +++ b/cmd/api/src/api/tools/analysis_schedule_test.go @@ -1,3 +1,19 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + package tools_test import ( diff --git a/cmd/api/src/api/v2/auth/oidc.go b/cmd/api/src/api/v2/auth/oidc.go index c71d6c26e5..8f3de55ec3 100644 --- a/cmd/api/src/api/v2/auth/oidc.go +++ b/cmd/api/src/api/v2/auth/oidc.go @@ -18,7 +18,6 @@ package auth import ( "net/http" - "strings" "github.com/specterops/bloodhound/src/utils/validation" @@ -42,17 +41,11 @@ func (s ManagementResource) CreateOIDCProvider(response http.ResponseWriter, req api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, err.Error(), request), response) } else if validated := validation.Validate(createRequest); validated != nil { api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, validated.Error(), request), response) - } else if strings.Contains(createRequest.Name, " ") { - api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, "invalid name formatting, ensure there are no spaces in the provided name", request), response) } else { - var ( - formattedName = strings.ToLower(createRequest.Name) - ) - - if provider, err := s.db.CreateOIDCProvider(request.Context(), formattedName, createRequest.Issuer, createRequest.ClientID); err != nil { + if oidcProvider, err := s.db.CreateOIDCProvider(request.Context(), createRequest.Name, createRequest.Issuer, createRequest.ClientID); err != nil { api.HandleDatabaseError(request, response, err) } else { - api.WriteBasicResponse(request.Context(), provider, http.StatusCreated, response) + api.WriteBasicResponse(request.Context(), oidcProvider, http.StatusCreated, response) } } } diff --git a/cmd/api/src/api/v2/auth/oidc_test.go b/cmd/api/src/api/v2/auth/oidc_test.go index 5bed75ba50..c3b5733c86 100644 --- a/cmd/api/src/api/v2/auth/oidc_test.go +++ b/cmd/api/src/api/v2/auth/oidc_test.go @@ -19,16 +19,12 @@ package auth_test import ( "fmt" "net/http" - - "github.com/specterops/bloodhound/src/model" - - "github.com/specterops/bloodhound/src/api/v2/auth" + "testing" "github.com/specterops/bloodhound/src/api/v2/apitest" + "github.com/specterops/bloodhound/src/api/v2/auth" + "github.com/specterops/bloodhound/src/model" "github.com/specterops/bloodhound/src/utils/test" - - "testing" - "go.uber.org/mock/gomock" ) @@ -43,19 +39,17 @@ func TestManagementResource_CreateOIDCProvider(t *testing.T) { defer mockCtrl.Finish() t.Run("successfully create a new OIDCProvider", func(t *testing.T) { - mockDB.EXPECT().CreateOIDCProvider(gomock.Any(), "test", "https://localhost/auth", "bloodhound").Return(model.OIDCProvider{ - Name: "", - ClientID: "", - Issuer: "", + mockDB.EXPECT().CreateOIDCProvider(gomock.Any(), "Bloodhound gang", "https://localhost/auth", "bloodhound").Return(model.OIDCProvider{ + ClientID: "bloodhound", + Issuer: "https://localhost/auth", }, nil) test.Request(t). WithMethod(http.MethodPost). WithURL(url). WithBody(auth.CreateOIDCProviderRequest{ - Name: "test", - Issuer: "https://localhost/auth", - + Name: "Bloodhound gang", + Issuer: "https://localhost/auth", ClientID: "bloodhound", }). OnHandlerFunc(resources.CreateOIDCProvider). @@ -78,8 +72,9 @@ func TestManagementResource_CreateOIDCProvider(t *testing.T) { WithMethod(http.MethodPost). WithURL(url). WithBody(auth.CreateOIDCProviderRequest{ - Name: "test", - Issuer: "", + Name: "test", + Issuer: "1234:not:a:url", + ClientID: "bloodhound", }). OnHandlerFunc(resources.CreateOIDCProvider). Require(). @@ -106,9 +101,8 @@ func TestManagementResource_CreateOIDCProvider(t *testing.T) { WithMethod(http.MethodPost). WithURL(url). WithBody(auth.CreateOIDCProviderRequest{ - Name: "test", - Issuer: "https://localhost/auth", - + Name: "test", + Issuer: "https://localhost/auth", ClientID: "bloodhound", }). OnHandlerFunc(resources.CreateOIDCProvider). diff --git a/cmd/api/src/database/auth.go b/cmd/api/src/database/auth.go index d365aa8991..3a44895719 100644 --- a/cmd/api/src/database/auth.go +++ b/cmd/api/src/database/auth.go @@ -26,6 +26,7 @@ import ( "strings" "time" + "github.com/specterops/bloodhound/src/database/types/null" "gorm.io/gorm" "github.com/gofrs/uuid" @@ -464,7 +465,8 @@ func (s *BloodhoundDB) DeleteAuthSecret(ctx context.Context, authSecret model.Au }) } -// CreateSAMLProvider creates a new saml_providers row using the data in the input struct +// CreateSAMLIdentityProvider creates a new saml_providers row using the data in the input struct +// This also creates the corresponding sso_provider entry // INSERT INTO saml_identity_providers (...) VALUES (...) func (s *BloodhoundDB) CreateSAMLIdentityProvider(ctx context.Context, samlProvider model.SAMLProvider) (model.SAMLProvider, error) { auditEntry := model.AuditEntry{ @@ -473,7 +475,15 @@ func (s *BloodhoundDB) CreateSAMLIdentityProvider(ctx context.Context, samlProvi } err := s.AuditableTransaction(ctx, auditEntry, func(tx *gorm.DB) error { - return CheckError(tx.WithContext(ctx).Create(&samlProvider)) + bhdb := NewBloodhoundDB(tx, s.idResolver) + + // Create the associated SSO provider + if ssoProvider, err := bhdb.CreateSSOProvider(ctx, samlProvider.Name, model.SessionAuthProviderSAML); err != nil { + return err + } else { + samlProvider.SSOProviderID = null.Int32From(ssoProvider.ID) + return CheckError(tx.WithContext(ctx).Create(&samlProvider)) + } }) return samlProvider, err diff --git a/cmd/api/src/database/db.go b/cmd/api/src/database/db.go index 5c00f9a08c..219db85e6d 100644 --- a/cmd/api/src/database/db.go +++ b/cmd/api/src/database/db.go @@ -134,6 +134,7 @@ type Database interface { DeleteSAMLProvider(ctx context.Context, samlProvider model.SAMLProvider) error // SSO + SSOProviderData OIDCProviderData // Sessions diff --git a/cmd/api/src/database/migration/migrations/v6.1.0.sql b/cmd/api/src/database/migration/migrations/v6.1.0.sql index 749e5a1d8e..147ffba4b2 100644 --- a/cmd/api/src/database/migration/migrations/v6.1.0.sql +++ b/cmd/api/src/database/migration/migrations/v6.1.0.sql @@ -14,29 +14,84 @@ -- -- SPDX-License-Identifier: Apache-2.0 +-- Add Scheduled Analysis Configs +INSERT INTO parameters (key, name, description, value, created_at, updated_at) +VALUES ('analysis.scheduled', + 'Scheduled Analysis', + 'This configuration parameter allows setting a schedule for analysis. When enabled, analysis will only run when the scheduled time arrives', + '{ + "enabled": false, + "rrule": "" + }', + current_timestamp, current_timestamp) +ON CONFLICT DO NOTHING; + +-- Add last analysis time to datapipe status so we can track scheduled analysis time properly +ALTER TABLE datapipe_status + ADD COLUMN IF NOT EXISTS "last_analysis_run_at" TIMESTAMP with time zone; + +-- SSO Provider +CREATE TABLE IF NOT EXISTS sso_providers +( + id SERIAL PRIMARY KEY, + name TEXT NOT NULL, + slug TEXT NOT NULL, + type INTEGER NOT NULL, + + updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), + created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), + + UNIQUE (name), + UNIQUE (slug) +); + -- OIDC Provider CREATE TABLE IF NOT EXISTS oidc_providers ( - id BIGSERIAL PRIMARY KEY, - name TEXT NOT NULL, - client_id TEXT NOT NULL, - issuer TEXT NOT NULL, - - updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), - created_at TIMESTAMP WITH TIME ZONE DEFAULT now(), + id SERIAL PRIMARY KEY, + client_id TEXT NOT NULL, + issuer TEXT NOT NULL, + sso_provider_id INTEGER REFERENCES sso_providers (id) ON DELETE CASCADE NULL, - UNIQUE (name) + updated_at TIMESTAMP WITH TIME ZONE DEFAULT now(), + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() ); +-- Create the reference from saml_providers to sso_providers +ALTER TABLE ONLY saml_providers + ADD COLUMN IF NOT EXISTS sso_provider_id INTEGER NULL; +ALTER TABLE ONLY saml_providers + DROP CONSTRAINT IF EXISTS fk_saml_provider_sso_provider; +ALTER TABLE ONLY saml_providers + ADD CONSTRAINT fk_saml_provider_sso_provider FOREIGN KEY (sso_provider_id) REFERENCES sso_providers (id) ON DELETE CASCADE; --- Add Scheduled Analysis Configs -INSERT INTO parameters (key, name, description, value, created_at, updated_at) - VALUES ('analysis.scheduled', - 'Scheduled Analysis', - 'This configuration parameter allows setting a schedule for analysis. When enabled, analysis will only run when the scheduled time arrives', - '{"enabled": false, "rrule": ""}', - current_timestamp,current_timestamp) ON CONFLICT DO NOTHING; +-- Backfill our sso_providers table with the existing data from saml_providers +-- The hardcoded type is determined by the AuthProvider +-- See: +-- https://github.com/SpecterOps/BloodHound/blob/main/cmd/api/src/model/auth.go#L565 +INSERT INTO sso_providers(name, slug, type) (SELECT name, lower(replace(name, ' ', '-')), 1 + FROM saml_providers + WHERE sso_provider_id IS NULL) +ON CONFLICT DO NOTHING; --- Add last analysis time to datapipe status so we can track scheduled analysis time properly -ALTER TABLE datapipe_status -ADD COLUMN IF NOT EXISTS "last_analysis_run_at" TIMESTAMP with time zone; +-- Backfill the references from the newly created sso_provider entries +UPDATE saml_providers +SET sso_provider_id = (SELECT id FROM sso_providers WHERE name = saml_providers.name) +WHERE saml_providers.sso_provider_id IS NULL; + +-- Add the sso_provider to the users table +ALTER TABLE ONLY users + ADD COLUMN IF NOT EXISTS sso_provider_id INTEGER NULL; +ALTER TABLE ONLY users + DROP CONSTRAINT IF EXISTS fk_users_sso_provider; +ALTER TABLE ONLY users + ADD CONSTRAINT fk_users_sso_provider FOREIGN KEY (sso_provider_id) REFERENCES sso_providers (id) ON DELETE SET NULL; + +-- Backfill users with their proper sso_provider when they have a saml_provider_id +UPDATE users u +SET sso_provider_id = (SELECT sso.id + FROM saml_providers saml + JOIN sso_providers sso ON sso.id = saml.sso_provider_id + WHERE u.saml_provider_id = saml.id) +WHERE sso_provider_id IS NULL + AND saml_provider_id IS NOT NULL; \ No newline at end of file diff --git a/cmd/api/src/database/mocks/db.go b/cmd/api/src/database/mocks/db.go index 801c641738..6159f55afb 100644 --- a/cmd/api/src/database/mocks/db.go +++ b/cmd/api/src/database/mocks/db.go @@ -303,6 +303,21 @@ func (mr *MockDatabaseMockRecorder) CreateSAMLIdentityProvider(arg0, arg1 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSAMLIdentityProvider", reflect.TypeOf((*MockDatabase)(nil).CreateSAMLIdentityProvider), arg0, arg1) } +// CreateSSOProvider mocks base method. +func (m *MockDatabase) CreateSSOProvider(arg0 context.Context, arg1 string, arg2 model.SessionAuthProvider) (model.SSOProvider, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "CreateSSOProvider", arg0, arg1, arg2) + ret0, _ := ret[0].(model.SSOProvider) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// CreateSSOProvider indicates an expected call of CreateSSOProvider. +func (mr *MockDatabaseMockRecorder) CreateSSOProvider(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateSSOProvider", reflect.TypeOf((*MockDatabase)(nil).CreateSSOProvider), arg0, arg1, arg2) +} + // CreateSavedQuery mocks base method. func (m *MockDatabase) CreateSavedQuery(arg0 context.Context, arg1 uuid.UUID, arg2, arg3, arg4 string) (model.SavedQuery, error) { m.ctrl.T.Helper() diff --git a/cmd/api/src/database/oidc.go b/cmd/api/src/database/oidc_providers.go similarity index 50% rename from cmd/api/src/database/oidc.go rename to cmd/api/src/database/oidc_providers.go index 0c8d776b94..f66066ba47 100644 --- a/cmd/api/src/database/oidc.go +++ b/cmd/api/src/database/oidc_providers.go @@ -20,6 +20,11 @@ import ( "context" "github.com/specterops/bloodhound/src/model" + "gorm.io/gorm" +) + +const ( + oidcProvidersTableName = "oidc_providers" ) // OIDCProviderData defines the interface required to interact with the oidc_providers table @@ -27,13 +32,32 @@ type OIDCProviderData interface { CreateOIDCProvider(ctx context.Context, name, issuer, clientID string) (model.OIDCProvider, error) } -// CreateOIDCProvider creates a new entry for an OIDC provider +// CreateOIDCProvider creates a new entry for an OIDC provider as well as the associated SSO provider func (s *BloodhoundDB) CreateOIDCProvider(ctx context.Context, name, issuer, clientID string) (model.OIDCProvider, error) { - provider := model.OIDCProvider{ - Name: name, - ClientID: clientID, - Issuer: issuer, - } + var ( + oidcProvider = model.OIDCProvider{ + ClientID: clientID, + Issuer: issuer, + } + + auditEntry = model.AuditEntry{ + Action: model.AuditLogActionCreateOIDCIdentityProvider, + Model: &oidcProvider, // Pointer is required to ensure success log contains updated fields after transaction + } + ) + + // Create both the sso_providers and oidc_providers rows in a single transaction + // If one of these requests errors, both changes will be rolled back + err := s.AuditableTransaction(ctx, auditEntry, func(tx *gorm.DB) error { + bhdb := NewBloodhoundDB(tx, s.idResolver) + + if ssoProvider, err := bhdb.CreateSSOProvider(ctx, name, model.SessionAuthProviderOIDC); err != nil { + return err + } else { + oidcProvider.SSOProviderID = int(ssoProvider.ID) + return CheckError(tx.WithContext(ctx).Table(oidcProvidersTableName).Create(&oidcProvider)) + } + }) - return provider, CheckError(s.db.WithContext(ctx).Table("oidc_providers").Create(&provider)) + return oidcProvider, err } diff --git a/cmd/api/src/database/oidc_test.go b/cmd/api/src/database/oidc_providers_test.go similarity index 84% rename from cmd/api/src/database/oidc_test.go rename to cmd/api/src/database/oidc_providers_test.go index 12993dd563..3bd7532fbb 100644 --- a/cmd/api/src/database/oidc_test.go +++ b/cmd/api/src/database/oidc_providers_test.go @@ -22,7 +22,9 @@ package database_test import ( "context" "testing" + "time" + "github.com/specterops/bloodhound/src/model" "github.com/stretchr/testify/assert" "github.com/specterops/bloodhound/src/test/integration" @@ -40,8 +42,11 @@ func TestBloodhoundDB_CreateOIDCProvider(t *testing.T) { provider, err := dbInst.CreateOIDCProvider(testCtx, "test", "https://test.localhost.com/auth", "bloodhound") require.NoError(t, err) - assert.Equal(t, "test", provider.Name) assert.Equal(t, "https://test.localhost.com/auth", provider.Issuer) assert.Equal(t, "bloodhound", provider.ClientID) + + _, count, err := dbInst.ListAuditLogs(testCtx, time.Now().Add(-time.Minute), time.Now().Add(time.Minute), 0, 10, "", model.SQLFilter{}) + require.NoError(t, err) + assert.Equal(t, 2, count) }) } diff --git a/cmd/api/src/database/sso_providers.go b/cmd/api/src/database/sso_providers.go new file mode 100644 index 0000000000..9f6b02e05c --- /dev/null +++ b/cmd/api/src/database/sso_providers.go @@ -0,0 +1,45 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package database + +import ( + "context" + "strings" + + "github.com/specterops/bloodhound/src/model" +) + +const ( + ssoProviderTableName = "sso_providers" +) + +// SSOProviderData defines the methods required to interact with the sso_providers table +type SSOProviderData interface { + CreateSSOProvider(ctx context.Context, name string, authProvider model.SessionAuthProvider) (model.SSOProvider, error) +} + +// CreateSSOProvider creates an entry in the sso_providers table +// A slug will be created for the SSO Provider using the name argument as a base. The name will be lower cased and all spaces are replaced with `-` +func (s *BloodhoundDB) CreateSSOProvider(ctx context.Context, name string, authProvider model.SessionAuthProvider) (model.SSOProvider, error) { + provider := model.SSOProvider{ + Name: name, + Slug: strings.ToLower(strings.ReplaceAll(name, " ", "-")), + Type: authProvider, + } + + return provider, CheckError(s.db.WithContext(ctx).Table(ssoProviderTableName).Create(&provider)) +} diff --git a/cmd/api/src/database/sso_providers_test.go b/cmd/api/src/database/sso_providers_test.go new file mode 100644 index 0000000000..244d28772c --- /dev/null +++ b/cmd/api/src/database/sso_providers_test.go @@ -0,0 +1,47 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +//go:build integration +// +build integration + +package database_test + +import ( + "context" + "testing" + + "github.com/specterops/bloodhound/src/model" + "github.com/specterops/bloodhound/src/test/integration" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestBloodhoundDB_CreateSSOProvider(t *testing.T) { + var ( + testCtx = context.Background() + dbInst = integration.SetupDB(t) + ) + defer dbInst.Close(testCtx) + + t.Run("successfully create an SSO provider", func(t *testing.T) { + result, err := dbInst.CreateSSOProvider(testCtx, "Bloodhound Gang", model.SessionAuthProviderSAML) + require.NoError(t, err) + + assert.Equal(t, "Bloodhound Gang", result.Name) + assert.Equal(t, "bloodhound-gang", result.Slug) + assert.Equal(t, model.SessionAuthProviderSAML, result.Type) + }) +} diff --git a/cmd/api/src/model/audit.go b/cmd/api/src/model/audit.go index ddeb85c855..7b4fb3c223 100644 --- a/cmd/api/src/model/audit.go +++ b/cmd/api/src/model/audit.go @@ -64,6 +64,8 @@ const ( AuditLogActionUpdateSAMLIdentityProvider AuditLogAction = "UpdateSAMLIdentityProvider" AuditLogActionDeleteSAMLIdentityProvider AuditLogAction = "DeleteSAMLIdentityProvider" + AuditLogActionCreateOIDCIdentityProvider AuditLogAction = "CreateOIDCIdentityProvider" + AuditLogActionAcceptRisk AuditLogAction = "AcceptRisk" AuditLogActionUnacceptRisk AuditLogAction = "UnacceptRisk" diff --git a/cmd/api/src/model/auth.go b/cmd/api/src/model/auth.go index 5ae2be5a9c..3a6359a6ba 100644 --- a/cmd/api/src/model/auth.go +++ b/cmd/api/src/model/auth.go @@ -296,6 +296,8 @@ type SAMLProvider struct { ServiceProviderMetadataURI serde.URL `json:"sp_metadata_uri" gorm:"-"` ServiceProviderACSURI serde.URL `json:"sp_acs_uri" gorm:"-"` + SSOProviderID null.Int32 `json:"sso_provider_id"` + Serial } @@ -466,6 +468,8 @@ type User struct { // This value is automatically set to true for Bloodhound Community Edition in the patchEULAAcceptance and CreateUser functions. EULAAccepted bool `json:"eula_accepted"` + SSOProviderID null.Int32 `json:"sso_provider_id,omitempty"` + Unique } @@ -565,6 +569,7 @@ type SessionAuthProvider int const ( SessionAuthProviderSecret SessionAuthProvider = 0 SessionAuthProviderSAML SessionAuthProvider = 1 + SessionAuthProviderOIDC SessionAuthProvider = 2 ) type SessionFlagKey string diff --git a/cmd/api/src/model/oidc_provider.go b/cmd/api/src/model/oidc_provider.go new file mode 100644 index 0000000000..a161570688 --- /dev/null +++ b/cmd/api/src/model/oidc_provider.go @@ -0,0 +1,35 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package model + +// OIDCProvider contains the data needed to initiate an OIDC secure login flow +type OIDCProvider struct { + ClientID string `json:"client_id"` + Issuer string `json:"issuer"` + SSOProviderID int `json:"sso_provider_id"` + + Serial +} + +func (s OIDCProvider) AuditData() AuditData { + return AuditData{ + "id": s.ID, + "client_id": s.ClientID, + "issuer": s.Issuer, + "sso_provider_id": s.SSOProviderID, + } +} diff --git a/cmd/api/src/model/oidc.go b/cmd/api/src/model/sso_provider.go similarity index 68% rename from cmd/api/src/model/oidc.go rename to cmd/api/src/model/sso_provider.go index a461ea613f..05757ab2db 100644 --- a/cmd/api/src/model/oidc.go +++ b/cmd/api/src/model/sso_provider.go @@ -16,10 +16,11 @@ package model -type OIDCProvider struct { - Name string `json:"name"` - ClientID string `json:"client_id"` - Issuer string `json:"issuer"` +// SSOProvider is the common representation of an SSO provider that can be used to display high level information about that provider +type SSOProvider struct { + Type SessionAuthProvider `json:"type"` + Name string `json:"name"` + Slug string `json:"slug"` - BigSerial + Serial } diff --git a/packages/go/graphschema/ad/ad.go b/packages/go/graphschema/ad/ad.go index cef7bba525..27e38f0da3 100644 --- a/packages/go/graphschema/ad/ad.go +++ b/packages/go/graphschema/ad/ad.go @@ -21,7 +21,6 @@ package ad import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) diff --git a/packages/go/graphschema/azure/azure.go b/packages/go/graphschema/azure/azure.go index 787ee392e6..00b20f190f 100644 --- a/packages/go/graphschema/azure/azure.go +++ b/packages/go/graphschema/azure/azure.go @@ -21,7 +21,6 @@ package azure import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) diff --git a/packages/go/graphschema/common/common.go b/packages/go/graphschema/common/common.go index 9320bb8d29..6fd161585e 100644 --- a/packages/go/graphschema/common/common.go +++ b/packages/go/graphschema/common/common.go @@ -21,7 +21,6 @@ package common import ( "errors" - graph "github.com/specterops/bloodhound/dawgs/graph" ) diff --git a/packages/go/openapi/doc/openapi.json b/packages/go/openapi/doc/openapi.json index c06b84f53d..2c84504df9 100644 --- a/packages/go/openapi/doc/openapi.json +++ b/packages/go/openapi/doc/openapi.json @@ -12881,15 +12881,14 @@ }, "query.entity.type": { "name": "type", - "description": "The type of return data requested. If no type is provided, query will default to `list`.\nThe only supported type is `list`, but unsupported `graph` and `count` can be used.\nAn invalid type will result in a `count` query. Some entity query endpoints do not\nsupport the `graph` type.\n", + "description": "The type of return data requested. If no type is provided, query will default to `list`.\nThe only supported type is `list`, but the unsupported `graph` type can be used.\nSome entity query endpoints do not support the `graph` type. For those interested in\nusing the undocumented graph type parameter, the response type is described in the schema\n`model.bh-graph.graph`.\n", "in": "query", "schema": { "type": "string", "default": "list", "enum": [ "list", - "graph", - "count" + "graph" ] } }, @@ -15709,7 +15708,7 @@ } }, "related-entity-query-results": { - "description": "**OK**\n\nThis endpoint returns a response, dependent upon which return type is requested by the `type` parameter.\nThe only supported `type` parameter is `list`.\nWhile `list` is the only supported `type` parameter, the `count` or `graph` parameters can be used\nand will result in a different response structure then documented here.\nFor those interested in using the undocumented graph type parameter, the response type is described in the schema\nmodel.bh-graph.graph.\n", + "description": "**OK**\n\nThis endpoint returns a response, dependent upon which return type is requested by the `type` parameter.\nThe only supported `type` parameter is `list`.\nWhile `list` is the only supported `type` parameter, the `graph` parameter can be used\nand will result in a different response structure then documented here.\nFor those interested in using the undocumented graph type parameter, the response type is described in the schema\n`model.bh-graph.graph`.\n", "content": { "application/json": { "schema": {