Files
linoman b7a0aeeb0d SCIM: Disable auto assign organization if the user has been provisioned (#101307)
* Add isProvisioned field to model

* Add new isProvisioned column to migration

* Disable auto assignment to organization if the user is provisioned

* add annotation to user model

* add annotation to user models

* Remove IsProvisioned field from Identity

* Move new field assignenment and add default value

* Update annotations for user query results

* Remove isProvisioned from identity

* Add new column to test

* Resolve user from identity at SyncOrgHook
2025-03-03 17:51:23 +01:00

172 lines
4.6 KiB
Go

package dbimpl
import (
"context"
"database/sql"
"net/http"
"sync"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel/trace"
traceNoop "go.opentelemetry.io/otel/trace/noop"
ini "gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/bus"
infraDB "github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
)
type (
// cfgSectionMap represents an INI section, mapping from an INI key to an
// INI value.
cfgSectionMap = map[string]string
// cfgMap is a map from INI section name to INI section contents.
cfgMap = map[string]cfgSectionMap
)
// setupDBForGrafana modifies `m` in the following way:
//
// [database]
// type = sqlite3
// path = unique-random-path
//
// After that, it initializes a temporary SQLite filesystem-backed database that
// is later deleted when the test finishes.
func setupDBForGrafana(t *testing.T, ctx context.Context, m cfgMap) {
dbSection, ok := m["database"]
if !ok {
dbSection = cfgSectionMap{}
m["database"] = dbSection
}
dbSection["type"] = "sqlite3"
dbSection["path"] = t.TempDir() + "/" + uuid.New().String()
db, err := sql.Open("sqlite3", "file:"+dbSection["path"])
require.NoError(t, err)
_, err = db.ExecContext(ctx, `
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
version INTEGER NOT NULL,
login TEXT NOT NULL,
email TEXT NOT NULL,
name TEXT NULL,
password TEXT NULL,
salt TEXT NULL,
rands TEXT NULL,
company TEXT NULL,
org_id INTEGER NOT NULL,
is_admin INTEGER NOT NULL,
email_verified INTEGER NULL,
theme TEXT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL,
help_flags1 INTEGER NOT NULL DEFAULT 0,
last_seen_at DATETIME NULL,
is_disabled INTEGER NOT NULL DEFAULT 0,
is_service_account BOOLEAN DEFAULT 0,
is_provisioned BOOLEAN DEFAULT 0,
uid TEXT NULL
);
CREATE TABLE org (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
version INTEGER NOT NULL,
name TEXT NOT NULL,
address1 TEXT NULL,
address2 TEXT NULL,
city TEXT NULL,
state TEXT NULL,
zip_code TEXT NULL,
country TEXT NULL,
billing_email TEXT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL
);
CREATE TABLE org_user (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
org_id INTEGER NOT NULL,
user_id INTEGER NOT NULL,
role TEXT NOT NULL,
created DATETIME NOT NULL,
updated DATETIME NOT NULL
);
`)
require.NoError(t, err)
}
func newTestInfraDB(t *testing.T, m cfgMap) infraDB.DB {
t.Helper()
// nil migrations means no migrations
sqlstoreDB, err := sqlstore.ProvideService(
newCfgFromIniMap(t, m), // *setting.Cfg
featureTogglesNop{}, // featuremgmt.FeatureToggles
nil, // registry.DatabaseMigrator
nopBus{}, // github.com/grafana/grafana/pkg/bus.Bus
newNopTestGrafanaTracer(),
)
require.NoError(t, err)
return sqlstoreDB
}
// globalUnprotectedMutableState controls access to global mutable state found
// in the `setting` package that is not appropriately protected. This would
// ideally be a part of some struct instead of being global, be protected if it
// needs to change, and be unmutable once it no longer needs to change. Example:
// `setting.AppUrl`. Nothing can run in parallel because of this.
// TODO: fix that.
var globalUnprotectedMutableState sync.Mutex
func newCfgFromIniMap(t *testing.T, m cfgMap) *setting.Cfg {
t.Helper()
globalUnprotectedMutableState.Lock()
defer globalUnprotectedMutableState.Unlock()
cfg, err := setting.NewCfgFromINIFile(newTestINIFile(t, m))
require.NoError(t, err)
return cfg
}
func newTestINIFile(t *testing.T, m cfgMap) *ini.File {
t.Helper()
f := ini.Empty()
for sectionName, kvs := range m {
section, err := f.NewSection(sectionName)
require.NoError(t, err)
for k, v := range kvs {
_, err := section.NewKey(k, v)
require.NoError(t, err)
}
}
return f
}
type (
testGrafanaTracer struct {
trace.Tracer
}
featureTogglesNop struct{}
nopBus struct{}
)
func (testGrafanaTracer) Inject(context.Context, http.Header, trace.Span) {}
func newNopTestGrafanaTracer() tracing.Tracer {
return testGrafanaTracer{traceNoop.NewTracerProvider().Tracer("test")}
}
func (featureTogglesNop) IsEnabled(context.Context, string) bool {
return false
}
func (featureTogglesNop) IsEnabledGlobally(string) bool {
return false
}
func (featureTogglesNop) GetEnabled(context.Context) map[string]bool {
return map[string]bool{}
}
func (nopBus) Publish(context.Context, bus.Msg) error { return nil }
func (nopBus) AddEventListener(bus.HandlerFunc) {}