Merge pull request #93 from RafayLabs/system-audit

System audit logs
This commit is contained in:
nirav-rafay
2022-04-11 17:33:51 +05:30
committed by GitHub
44 changed files with 3306 additions and 919 deletions
+46
View File
@@ -0,0 +1,46 @@
# Filebeat
We use filebeat inorder to push logs from application to elasticsearch
server.
In actual deployment, the idea is that we will have a sidecar
container that will be responsible for tailing the log files so that
we can read it using a filebeat instance running in a daemonset.
## Development
For local testing, you can run filebeat as as a binary and push the
logs generated into ES. You can use the following config to do so.
``` yaml
filebeat.inputs:
- type: log
fields:
type: "auditlogs"
paths:
- audit.log # audit file path
json.keys_under_root: true
json.overwrite_keys: true
json.add_error_key: true
json.expand_keys: true
output.elasticsearch:
hosts: ["http://127.0.0.1:9200"]
index: "index-%{[fields.type]:other}-%{+yyyy.MM.dd}"
ssl:
verification_mode: "none"
enabled: false
ca_trusted_fingerprint: "ignore-this"
setup.template.name: "index"
setup.template.pattern: "index-*"
setup.template.overwrite: true
setup.template.append_fields:
- name: timestamp
type: date
```
This will push the audit logs generated in `audit.log` file into
elasticsearch index with the name `index-auditlogs-<date>` which you
should be able to see in ES. Now if you were to use this as the audit
index key, you should be able to use this in the application.
@@ -203,6 +203,15 @@
"clientType": {
"$ref": "#/definitions/v3ClientType"
},
"clientHost": {
"type": "string"
},
"clientIp": {
"type": "string"
},
"clientUa": {
"type": "string"
},
"isAllNsAccess": {
"type": "object",
"additionalProperties": {
+90
View File
@@ -54,22 +54,43 @@ require (
)
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/DataDog/datadog-go v4.8.2+incompatible // indirect
github.com/DataDog/sketches-go v1.2.1 // indirect
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
github.com/beevik/etree v1.1.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff v2.2.1+incompatible // indirect
github.com/cenkalti/backoff/v4 v4.1.2 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect
github.com/containerd/continuity v0.2.1 // indirect
github.com/crewjam/httperr v0.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denisenkom/go-mssqldb v0.11.0 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/docker/cli v20.10.11+incompatible // indirect
github.com/docker/docker v20.10.9+incompatible // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/eapache/go-resiliency v1.2.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/elastic/go-licenser v0.3.1 // indirect
github.com/elastic/go-sysinfo v1.7.1 // indirect
github.com/elastic/go-windows v1.0.1 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/fatih/structs v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fxamacker/cbor/v2 v2.2.0 // indirect
github.com/go-logr/logr v1.2.0 // indirect
github.com/go-openapi/analysis v0.21.2 // indirect
github.com/go-openapi/jsonpointer v0.19.5 // indirect
@@ -78,21 +99,31 @@ require (
github.com/go-openapi/spec v0.20.4 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gobuffalo/pop/v5 v5.3.4 // indirect
github.com/gofrs/uuid v4.1.0+incompatible // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.1.0 // indirect
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
github.com/golang/gddo v0.0.0-20190904175337-72a348e765d2 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.0.21 // indirect
github.com/google/go-cmp v0.5.7 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/googleapis/gnostic v0.5.5 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.2.1 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/inhies/go-bytesize v0.0.0-20210819104631-275770b98743 // indirect
github.com/instana/go-sensor v1.34.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.10.1-0.20211002123621-290ee79d1e8d // indirect
github.com/jackc/pgio v1.0.0 // indirect
@@ -101,6 +132,8 @@ require (
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
github.com/jackc/pgtype v1.8.1 // indirect
github.com/jackc/pgx/v4 v4.13.0 // indirect
github.com/jandelgado/gcov2lcov v1.0.5 // indirect
github.com/jcchavezs/porto v0.3.0 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
@@ -108,59 +141,116 @@ require (
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jmoiron/sqlx v1.3.4 // indirect
github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/julienschmidt/httprouter v1.3.0 // indirect
github.com/klauspost/compress v1.14.4 // indirect
github.com/knadh/koanf v1.3.0 // indirect
github.com/lib/pq v1.10.3 // indirect
github.com/looplab/fsm v0.3.0 // indirect
github.com/magiconair/properties v1.8.5 // 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.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.4.3 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.2 // indirect
github.com/opencontainers/runc v1.0.2 // indirect
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5 // indirect
github.com/openzipkin/zipkin-go v0.2.5 // indirect
github.com/ory/dockertest/v3 v3.8.1 // indirect
github.com/ory/go-acc v0.2.6 // indirect
github.com/ory/herodot v0.9.12 // indirect
github.com/ory/jsonschema/v3 v3.0.4 // indirect
github.com/ory/kratos v0.8.2-alpha.1 // indirect
github.com/ory/nosurf v1.2.6 // indirect
github.com/ory/viper v1.7.5 // indirect
github.com/ory/x v0.0.310 // indirect
github.com/pborman/uuid v1.2.1 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/philhofer/fwd v1.1.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rs/cors v1.8.0 // indirect
github.com/russellhaering/goxmldsig v1.1.1 // indirect
github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
github.com/satori/go.uuid v1.2.0 // indirect
github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect
github.com/segmentio/asm v1.1.3 // indirect
github.com/sirupsen/logrus v1.8.1 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/cobra v1.2.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/gjson v1.9.4 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tinylib/msgp v1.1.6 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/uber/jaeger-client-go v2.29.1+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.elastic.co/apm v1.14.0 // indirect
go.elastic.co/apm/module/apmhttp v1.14.0 // indirect
go.elastic.co/apm/module/apmot v1.14.0 // indirect
go.elastic.co/fastjson v1.1.0 // indirect
go.mongodb.org/mongo-driver v1.8.3 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.25.0 // indirect
go.opentelemetry.io/otel v1.0.1 // indirect
go.opentelemetry.io/otel/trace v1.0.1 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9 // indirect
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect
golang.org/x/tools v0.1.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
gorm.io/driver/mysql v1.1.2 // indirect
gorm.io/driver/sqlserver v1.2.1 // indirect
gorm.io/plugin/dbresolver v1.1.0 // indirect
howett.net/plist v0.0.0-20201203080718-1454fab16a06 // indirect
k8s.io/component-base v0.23.4 // indirect
k8s.io/klog v1.0.0 // indirect
k8s.io/klog/v2 v2.30.0 // indirect
+1574
View File
File diff suppressed because it is too large Load Diff
+44
View File
@@ -128,6 +128,22 @@ func GetNameById(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface
return entity, nil
}
func GetNamesByIds(ctx context.Context, db bun.IDB, id []uuid.UUID, entity interface{}) ([]string, error) {
names := []string{}
if len(id) == 0 {
return names, nil
}
err := db.NewSelect().Column("name").Model(entity).
Where("id = (?)", bun.In(id)).
Where("trash = ?", false).
Scan(ctx, &names)
if err != nil {
return nil, err
}
return names, nil
}
func Update(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) (interface{}, error) {
if _, err := db.NewUpdate().Model(entity).Where("id = ?", id).Exec(ctx); err != nil {
return nil, err
@@ -147,6 +163,7 @@ func Delete(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) e
Model(entity).
Column("trash").
Where("id = ?", id).
Where("trash = false").
Set("trash = ?", true).
Exec(ctx)
return err
@@ -157,11 +174,38 @@ func DeleteX(ctx context.Context, db bun.IDB, field string, value interface{}, e
Model(entity).
Column("trash").
Where("? = ?", bun.Ident(field), value).
Where("trash = false").
Set("trash = ?", true).
Exec(ctx)
return err
}
// DeleteR delete and returns the changed items
func DeleteR(ctx context.Context, db bun.IDB, id uuid.UUID, entity interface{}) error {
_, err := db.NewUpdate().
Model(entity).
Column("trash").
Where("id = ?", id).
Where("trash = false").
Set("trash = ?", true).
Returning("*").
Exec(ctx)
return err
}
// DeleteXR delete with selector and returns the changed items
func DeleteXR(ctx context.Context, db bun.IDB, field string, value interface{}, entity interface{}) error {
_, err := db.NewUpdate().
Model(entity).
Column("trash").
Where("? = ?", bun.Ident(field), value).
Where("trash = false").
Set("trash = ?", true).
Returning("*").
Exec(ctx)
return err
}
// HardDeleteAll deletes all records in a table (primarily for use in scripts)
func HardDeleteAll(ctx context.Context, db bun.IDB, entity interface{}) error {
_, err := db.NewDelete().
+14
View File
@@ -129,3 +129,17 @@ func ListFilteredUsers(ctx context.Context, db bun.IDB, users *[]models.KratosId
}
return users, nil
}
func GetUserNamesByIds(ctx context.Context, db bun.IDB, id []uuid.UUID, entity interface{}) ([]string, error) {
names := []string{}
if len(id) == 0 {
return names, nil
}
err := db.NewSelect().ColumnExpr("traits ->> 'email' as name").Model(entity).
Where("id = (?)", bun.In(id)).
Scan(ctx, &names)
if err != nil {
return nil, err
}
return names, nil
}
+24 -10
View File
@@ -14,6 +14,7 @@ import (
"github.com/RafayLabs/rcloud-base/internal/fixtures"
providers "github.com/RafayLabs/rcloud-base/internal/provider/kratos"
"github.com/RafayLabs/rcloud-base/pkg/audit"
authv3 "github.com/RafayLabs/rcloud-base/pkg/auth/v3"
"github.com/RafayLabs/rcloud-base/pkg/common"
"github.com/RafayLabs/rcloud-base/pkg/enforcer"
@@ -70,6 +71,7 @@ const (
relayImageEnv = "RELAY_IMAGE"
// audit
auditFileEnv = "AUDIT_LOG_FILE"
esEndPointEnv = "ES_END_POINT"
esIndexPrefixEnv = "ES_INDEX_PREFIX"
relayAuditESIndexPrefixEnv = "RELAY_AUDITS_ES_INDEX_PREFIX"
@@ -110,6 +112,7 @@ var (
relayImage string
// audit
auditFile string
elasticSearchUrl string
esIndexPrefix string
relayAuditsESIndexPrefix string
@@ -186,6 +189,7 @@ func setup() {
viper.SetDefault(esIndexPrefixEnv, "events-core")
viper.SetDefault(relayAuditESIndexPrefixEnv, "relay-audits")
viper.SetDefault(relayCommandESIndexPrefix, "relay-commands")
viper.SetDefault(auditFileEnv, "audit.log")
// cd relay
viper.SetDefault(coreCDRelayUserHostEnv, "*.user.cdrelay.rafay.local:10012")
@@ -217,6 +221,7 @@ func setup() {
viper.BindEnv(relayImageEnv)
viper.BindEnv(schedulerNamespaceEnv)
viper.BindEnv(auditFileEnv)
viper.BindEnv(esEndPointEnv)
viper.BindEnv(esIndexPrefixEnv)
viper.BindEnv(relayAuditESIndexPrefixEnv)
@@ -244,6 +249,7 @@ func setup() {
relayImage = viper.GetString(relayImageEnv)
schedulerNamespace = viper.GetString(schedulerNamespaceEnv)
auditFile = viper.GetString(auditFileEnv)
elasticSearchUrl = viper.GetString(esEndPointEnv)
esIndexPrefix = viper.GetString(esIndexPrefixEnv)
relayAuditsESIndexPrefix = viper.GetString(relayAuditESIndexPrefixEnv)
@@ -274,6 +280,14 @@ func setup() {
_log.Infow("printing db", "db", db)
ao := audit.AuditOptions{
LogPath: auditFile,
MaxSizeMB: 1,
MaxBackups: 10, // Should we let sidecar do rotation?
MaxAgeDays: 10, // Make these configurable via env
}
auditLogger := audit.GetAuditLogger(&ao)
// authz services
gormDb, err := gorm.Open(postgres.New(postgres.Config{
Conn: sqldb,
@@ -289,9 +303,9 @@ func setup() {
schedulerPool = schedulerrpc.NewSchedulerPool(schedulerAddr, 5*goruntime.NumCPU())
ps = service.NewPartnerService(db)
os = service.NewOrganizationService(db)
pps = service.NewProjectService(db, as)
ps = service.NewPartnerService(db, auditLogger)
os = service.NewOrganizationService(db, auditLogger)
pps = service.NewProjectService(db, as, auditLogger)
// users and role management services
cc := common.CliConfigDownloadData{
@@ -303,13 +317,13 @@ func setup() {
} else {
cc.Profile = "production"
}
ks = service.NewApiKeyService(db)
us = service.NewUserService(providers.NewKratosAuthProvider(kc), db, as, ks, cc)
gs = service.NewGroupService(db, as)
rs = service.NewRoleService(db, as)
ks = service.NewApiKeyService(db, auditLogger)
us = service.NewUserService(providers.NewKratosAuthProvider(kc), db, as, ks, cc, auditLogger)
gs = service.NewGroupService(db, as, auditLogger)
rs = service.NewRoleService(db, as, auditLogger)
rrs = service.NewRolepermissionService(db)
is = service.NewIdpService(db, apiAddr)
oidcs = service.NewOIDCProviderService(db, kratosAddr)
is = service.NewIdpService(db, apiAddr, auditLogger)
oidcs = service.NewOIDCProviderService(db, kratosAddr, auditLogger)
//sentry related services
bs = service.NewBootstrapService(db)
@@ -355,7 +369,7 @@ func setup() {
RelayAgentImage: relayImage,
}
cs = service.NewClusterService(db, downloadData, bs)
cs = service.NewClusterService(db, downloadData, bs, auditLogger)
ms = service.NewMetroService(db)
notify.Init(cs)
+28
View File
@@ -315,11 +315,20 @@ GET :host/auth/v3/users
Content-Type: application/yaml
X-Session-Token: :token
# Get all users with query options
GET :host/auth/v3/users?partner=:partner&organization=:org&q=user&name=john&order_by=email&project=ALL,:project
Content-Type: application/yaml
X-Session-Token: :token
# Get single user
GET :host/auth/v3/user/:user
Content-Type: application/yaml
X-Session-Token: :token
# Get currently logged in user info
GET :host/auth/v3/userinfo
Content-Type: application/yaml
X-Session-Token: :token
# Delete single user
DELETE :host/auth/v3/user/:user
@@ -477,6 +486,7 @@ X-Session-Token: :token
# Audit by project
GET :host/event/v1/auditlog?filter.timefrom=now-1h
X-Session-Token: :token
## Partner
@@ -510,6 +520,24 @@ metadata:
spec:
active: true
# Update organization
PUT :host/auth/v3/partner/:partner/organization/:org
Content-Type: application/yaml
X-Session-Token: :token
metadata:
partner: :partner
name: :org
description: "Very first organizataion"
spec:
active: true
settings:
idleLogoutMin: 30
lockout:
enabled: true
period_min: 10
attempts: 6
# List organizations
GET :host/auth/v3/partner/:partner/organizations
Content-Type: application/yaml
@@ -32,10 +32,6 @@ CREATE INDEX authsrv_projectaccountresourcerole_project_id_f8a43852 ON authsrv_p
CREATE INDEX authsrv_projectaccountresourcerole_role_id_a345b16f ON authsrv_projectaccountresourcerole USING btree (role_id);
ALTER TABLE ONLY authsrv_projectaccountresourcerole
ADD CONSTRAINT authsrv_projectaccou_account_id_532ce8df_fk_authsrv_a FOREIGN KEY (account_id)
REFERENCES identities(id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ONLY authsrv_projectaccountresourcerole
ADD CONSTRAINT authsrv_projectaccou_organization_id_91c5602d_fk_authsrv_o FOREIGN KEY (organization_id)
REFERENCES authsrv_organization(id) DEFERRABLE INITIALLY DEFERRED;
@@ -32,10 +32,6 @@ CREATE INDEX authsrv_projectaccountnamespacerole_project_id_66e567ed ON authsrv_
CREATE INDEX authsrv_projectaccountnamespacerole_role_id_8a5411cc ON authsrv_projectaccountnamespacerole USING btree (role_id);
ALTER TABLE ONLY authsrv_projectaccountnamespacerole
ADD CONSTRAINT authsrv_projectaccou_account_id_4fac0ac2_fk_authsrv_a FOREIGN KEY (account_id)
REFERENCES identities(id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ONLY authsrv_projectaccountnamespacerole
ADD CONSTRAINT authsrv_projectaccou_organization_id_96c921c9_fk_authsrv_o FOREIGN KEY (organization_id)
REFERENCES authsrv_organization(id) DEFERRABLE INITIALLY DEFERRED;
@@ -22,10 +22,6 @@ CREATE INDEX authsrv_groupaccount_name_d17de056 ON authsrv_groupaccount USING bt
CREATE INDEX authsrv_groupaccount_name_d17de056_like ON authsrv_groupaccount USING btree (name varchar_pattern_ops);
ALTER TABLE ONLY authsrv_groupaccount
ADD CONSTRAINT authsrv_groupaccount_account_id_041e4e98_fk_authsrv_account_id FOREIGN KEY (account_id)
REFERENCES identities(id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ONLY authsrv_groupaccount
ADD CONSTRAINT authsrv_groupaccount_group_id_c67750ef_fk_authsrv_group_id FOREIGN KEY (group_id)
REFERENCES authsrv_group(id) DEFERRABLE INITIALLY DEFERRED;
@@ -29,10 +29,6 @@ CREATE INDEX authsrv_accountresourcerole_partner_id_8e96aff4 ON authsrv_accountr
CREATE INDEX authsrv_accountresourcerole_role_id_769ec143 ON authsrv_accountresourcerole USING btree (role_id);
ALTER TABLE ONLY authsrv_accountresourcerole
ADD CONSTRAINT authsrv_accountresou_account_id_229069ae_fk_authsrv_a FOREIGN KEY (account_id)
REFERENCES identities(id) DEFERRABLE INITIALLY DEFERRED;
ALTER TABLE ONLY authsrv_accountresourcerole
ADD CONSTRAINT authsrv_accountresou_organization_id_22bb772c_fk_authsrv_o FOREIGN KEY (organization_id)
REFERENCES authsrv_organization(id) DEFERRABLE INITIALLY DEFERRED;
+74 -63
View File
@@ -2,7 +2,6 @@ package audit
import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
@@ -10,7 +9,7 @@ import (
logv2 "github.com/RafayLabs/rcloud-base/pkg/log"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
"github.com/Shopify/sarama"
"go.uber.org/zap"
"google.golang.org/grpc/metadata"
)
@@ -46,11 +45,12 @@ type EventActorAccount struct {
// EventActor Event's initiator
type EventActor struct {
Type string `json:"type"`
PartnerID string `json:"partner_id"`
OrganizationID string `json:"organization_id"`
Account EventActorAccount `json:"account"`
Groups []string `json:"groups"`
Type string `json:"type"`
// Add org and partner id once we have multi-org support
// PartnerID string `json:"partner_id"`
// OrganizationID string `json:"organization_id"`
Account EventActorAccount `json:"account"`
Groups []string `json:"groups"`
}
// EventClient Event's client
@@ -69,22 +69,19 @@ type EventDetail struct {
// Event is struct to hold event data
type Event struct {
Version EventVersion `json:"version"`
Category EventCategory `json:"category"`
Origin EventOrigin `json:"origin"`
Portal string `json:"portal"`
Type string `json:"type"`
PartnerID string `json:"partner_id"`
OrganizationID string `json:"organization_id"`
ProjectID string `json:"project_id"`
Actor *EventActor `json:"actor"`
Client *EventClient `json:"client"`
Detail *EventDetail `json:"detail"`
Timestamp string `json:"timestamp"`
Version EventVersion `json:"version"`
Category EventCategory `json:"category"`
Origin EventOrigin `json:"origin"`
Portal string `json:"portal"`
Type string `json:"type"`
ProjectID string `json:"project_id"`
Actor *EventActor `json:"actor"`
Client *EventClient `json:"client"`
Detail *EventDetail `json:"detail"`
Timestamp string `json:"timestamp"`
}
type createEventOptions struct {
producer sarama.AsyncProducer
version EventVersion
origin EventOrigin
category EventCategory
@@ -98,13 +95,6 @@ type createEventOptions struct {
groups []string
}
// WithProducer sets producer for audit event
func WithProducer(producer sarama.AsyncProducer) CreateEventOption {
return func(opts *createEventOptions) {
opts.producer = producer
}
}
// WithVersion sets version for audit event
func WithVersion(version EventVersion) CreateEventOption {
return func(opts *createEventOptions) {
@@ -186,18 +176,13 @@ func WithGroups(groups []string) CreateEventOption {
type CreateEventOption func(opts *createEventOptions)
// CreateEvent creates an event
func CreateEvent(event *Event, opts ...CreateEventOption) error {
func CreateEvent(al *zap.Logger, event *Event, opts ...CreateEventOption) error {
cOpts := createEventOptions{}
for _, opt := range opts {
opt(&cOpts)
}
if cOpts.producer == nil {
_log.Infow("audit event producer is nil")
return fmt.Errorf("audit even producer is nil")
}
t := time.Now()
dateArray := strings.Fields(t.String())
timestamp := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.%06d%s",
@@ -209,8 +194,6 @@ func CreateEvent(event *Event, opts ...CreateEventOption) error {
event.Category = cOpts.category
event.Origin = cOpts.origin
event.PartnerID = cOpts.partnerID
event.OrganizationID = cOpts.organizationID
event.ProjectID = cOpts.projectID
if event.Client == nil {
@@ -221,16 +204,7 @@ func CreateEvent(event *Event, opts ...CreateEventOption) error {
event.Actor = getActor(cOpts)
}
payload, err := json.Marshal(event)
if err != nil {
_log.Infow("unable to marshal audit event", "error", err)
return err
}
rawMessage := &sarama.ProducerMessage{
Topic: string(cOpts.topic),
Value: sarama.ByteEncoder(payload),
}
cOpts.producer.Input() <- rawMessage
go WriteEvent(event, al)
return nil
}
@@ -279,36 +253,25 @@ func getActor(cOpts createEventOptions) *EventActor {
Username: cOpts.username,
}
return &EventActor{
Type: "USER",
PartnerID: cOpts.partnerID,
OrganizationID: cOpts.organizationID,
Account: account,
Groups: cOpts.groups,
Type: "USER",
Account: account,
Groups: cOpts.groups,
}
}
func GetActorFromSessionData(sd *commonv3.SessionData) *EventActor {
pid := sd.GetPartner()
oid := sd.GetOrganization()
accountID := sd.GetAccount()
username := sd.GetUsername()
account := EventActorAccount{
ID: accountID,
Username: username,
}
groups := sd.Groups
// Set org id to string "null" for users with PARTNER_ADMIN role
if oid == "" {
oid = "null"
}
groups := sd.Groups // TODO: get groups (in interceptor?)
return &EventActor{
Type: "USER",
PartnerID: pid,
OrganizationID: oid,
Account: account,
Groups: groups,
Type: "USER",
Account: account,
Groups: groups,
}
}
@@ -321,6 +284,15 @@ func GetClientFromRequest(r *http.Request) *EventClient {
}
}
func GetClientFromSessionData(sd *commonv3.SessionData) *EventClient {
return &EventClient{
Type: "BROWSER",
IP: sd.GetClientIp(),
UserAgent: sd.GetClientUa(),
Host: sd.GetClientHost(),
}
}
func GetEvent(r *http.Request, sd *commonv3.SessionData, detail *EventDetail, eventType string, projectID string) *Event {
event := &Event{
Actor: GetActorFromSessionData(sd),
@@ -333,3 +305,42 @@ func GetEvent(r *http.Request, sd *commonv3.SessionData, detail *EventDetail, ev
return event
}
func CreateV1Event(al *zap.Logger, sd *commonv3.SessionData, detail *EventDetail, eventType string, projectID string) error {
actor := GetActorFromSessionData(sd)
client := GetClientFromSessionData(sd)
if projectID == "" {
projectID = "null"
}
event := &Event{
Version: VersionV1,
Category: AuditCategory,
Origin: OriginCore,
Actor: actor,
Client: client,
Detail: detail,
Type: eventType,
Portal: "OPS", // TODO: What is the portal?
ProjectID: projectID,
}
go WriteEvent(event, al)
return nil
}
func WriteEvent(event *Event, al *zap.Logger) {
al.Info(
"audit",
zap.String("version", string(event.Version)),
zap.String("category", string(event.Category)),
zap.String("origin", string(event.Origin)),
zap.Reflect("actor", event.Actor),
zap.Reflect("client", event.Client),
zap.Reflect("detail", event.Detail),
zap.String("type", event.Type),
zap.String("portal", event.Portal),
zap.String("project_id", event.ProjectID),
)
}
+34
View File
@@ -0,0 +1,34 @@
package audit
import (
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
// options holds audit options
type AuditOptions struct {
LogPath string
MaxSizeMB int
MaxBackups int
MaxAgeDays int
}
func GetAuditLogger(opts *AuditOptions) *zap.Logger {
encoder := zapcore.EncoderConfig{
TimeKey: "timestamp",
EncodeTime: zapcore.RFC3339NanoTimeEncoder,
}
logger := zap.New(zapcore.NewCore(
zapcore.NewJSONEncoder(encoder),
zapcore.AddSync(&lumberjack.Logger{
Filename: opts.LogPath,
MaxSize: opts.MaxSizeMB, // megabytes
MaxBackups: opts.MaxBackups,
MaxAge: opts.MaxAgeDays, // days
}),
zap.InfoLevel,
))
return logger
}
+3 -2
View File
@@ -10,6 +10,7 @@ import (
"github.com/RafayLabs/rcloud-base/pkg/service"
kclient "github.com/ory/kratos-client-go"
"github.com/uptrace/bun"
"go.uber.org/zap"
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
@@ -40,7 +41,7 @@ type authContext struct {
// SetupAuthContext sets up new authContext along with its
// dependencies. If the caller already has instances of authContext
// fields created then use NewAuthContext instead.
func SetupAuthContext() authContext {
func SetupAuthContext(auditLogger *zap.Logger) authContext {
var (
kc *kclient.APIClient
kratosScheme string
@@ -85,7 +86,7 @@ func SetupAuthContext() authContext {
}
as := service.NewAuthzService(db, enforcer)
return authContext{kc: kc, as: as, ks: service.NewApiKeyService(db)}
return authContext{kc: kc, as: as, ks: service.NewApiKeyService(db, auditLogger)}
}
func getEnvWithDefault(env, def string) string {
+18 -1
View File
@@ -59,6 +59,9 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
method string
token string
cookie string
host string
ua string
ip string
)
if len(md.Get(gateway.GatewayURL)) != 0 {
url = md.Get(gateway.GatewayURL)[0]
@@ -72,6 +75,16 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
if len(md.Get("grpcgateway-cookie")) != 0 {
cookie = md.Get("grpcgateway-cookie")[0]
}
if len(md.Get("x-gateway-host")) != 0 {
host = md.Get("x-gateway-host")[0]
}
if len(md.Get("x-gateway-user-agent")) != 0 {
ua = md.Get("x-gateway-user-agent")[0]
}
if len(md.Get("x-gateway-remote-addr")) != 0 {
ip = md.Get("x-gateway-remote-addr")[0]
}
acReq := &commonv3.IsRequestAllowedRequest{
Url: url,
Method: method,
@@ -89,7 +102,11 @@ func (ac authContext) NewAuthUnaryInterceptor(opt Option) grpc.UnaryServerInterc
s := res.GetStatus()
switch s {
case commonv3.RequestStatus_RequestAllowed:
ctx := context.WithValue(ctx, common.SessionDataKey, res.SessionData)
sd := res.SessionData
sd.ClientIp = ip
sd.ClientHost = host
sd.ClientUa = ua
ctx := context.WithValue(ctx, common.SessionDataKey, sd)
return handler(ctx, req)
case commonv3.RequestStatus_RequestMethodOrURLNotAllowed:
return nil, status.Error(codes.PermissionDenied, res.GetReason())
+3 -2
View File
@@ -12,6 +12,7 @@ import (
"github.com/google/uuid"
"github.com/uptrace/bun"
"github.com/urfave/negroni"
"go.uber.org/zap"
)
type authMiddleware struct {
@@ -20,9 +21,9 @@ type authMiddleware struct {
opt Option
}
func NewAuthMiddleware(opt Option) negroni.Handler {
func NewAuthMiddleware(al *zap.Logger, opt Option) negroni.Handler {
return &authMiddleware{
ac: SetupAuthContext(),
ac: SetupAuthContext(al),
opt: opt,
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ var (
// ErrInvalidAPIKey is returned when api key is invalid
ErrInvalidAPIKey = errors.New("invalid api key")
// ErrInvalidSignature is returns when signature is invalid
ErrInvalidSignature = errors.New("invaid signature")
ErrInvalidSignature = errors.New("invalid signature")
)
func (ac *authContext) IsRequestAllowed(ctx context.Context, httpreq *http.Request, req *commonv3.IsRequestAllowedRequest) (*commonv3.IsRequestAllowedResponse, error) {
+3 -3
View File
@@ -33,8 +33,8 @@ var rafayGatewayAnnotator = func(ctx context.Context, r *http.Request) metadata.
// }(),
GatewayAPIKey: r.Header.Get(GatewayAPIKey),
GatewayMethod: r.Method,
// UserAgent: r.UserAgent(),
// Host: r.Host,
// RemoteAddr: r.RemoteAddr,
UserAgent: r.UserAgent(),
Host: r.Host,
RemoteAddr: r.RemoteAddr,
})
}
+14 -3
View File
@@ -11,6 +11,7 @@ import (
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
"github.com/google/uuid"
"github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -31,11 +32,12 @@ type ApiKeyService interface {
// apiKeyService implements ApiKeyService
type apiKeyService struct {
db *bun.DB
al *zap.Logger
}
// NewApiKeyService return new api key service
func NewApiKeyService(db *bun.DB) ApiKeyService {
return &apiKeyService{db}
func NewApiKeyService(db *bun.DB, al *zap.Logger) ApiKeyService {
return &apiKeyService{db, al}
}
func (s *apiKeyService) Create(ctx context.Context, req *rpcv3.ApiKeyRequest) (*models.ApiKey, error) {
@@ -49,10 +51,14 @@ func (s *apiKeyService) Create(ctx context.Context, req *rpcv3.ApiKeyRequest) (*
Secret: crypto.GenerateSha256Secret(),
}
_, err := dao.Create(ctx, s.db, apikey)
entity, err := dao.Create(ctx, s.db, apikey)
if err != nil {
return nil, err
}
if ak, ok := entity.(*models.Group); ok {
CreateApiKeyAuditEvent(ctx, s.al, AuditActionCreate, ak.ID.String())
}
return apikey, nil
}
@@ -61,6 +67,11 @@ func (s *apiKeyService) Delete(ctx context.Context, req *rpcv3.ApiKeyRequest) (*
Set("trash = ?", true).
Where("account_id = ?", req.Username).
Where("key = ?", req.Id).Exec(ctx)
if err != nil {
return &rpcv3.DeleteUserResponse{}, err
}
CreateApiKeyAuditEvent(ctx, s.al, AuditActionDelete, req.Id)
return nil, err
}
+1 -12
View File
@@ -55,8 +55,6 @@ func getPrjectIdFromUrlScope(urlScope string) (string, error) {
func (a *AuditLogService) GetAuditLogByProjects(req *v1.AuditLogSearchRequest) (res *v1.AuditLogSearchResponse, err error) {
// No embedding in golang/protoc (https://github.com/golang/protobuf/issues/192)
oid := req.GetMetadata().GetOrganization() // TODO: these are to be filled in by authinterceptor
pid := req.GetMetadata().GetPartner()
err = validateQueryString(req.GetFilter().QueryString)
if err != nil {
return nil, err
@@ -72,16 +70,7 @@ func (a *AuditLogService) GetAuditLogByProjects(req *v1.AuditLogSearchRequest) (
"query": map[string]interface{}{
"bool": map[string]interface{}{
"must": []map[string]interface{}{
{
"term": map[string]interface{}{
"organization_id": oid,
},
},
{
"term": map[string]interface{}{
"partner_id": pid,
},
},
// Add org and partner filter once we have to support multi-org
{
"term": map[string]interface{}{
"category": "AUDIT",
+426
View File
@@ -0,0 +1,426 @@
package service
import (
"context"
"fmt"
"strings"
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
"github.com/RafayLabs/rcloud-base/pkg/audit"
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
"github.com/uptrace/bun"
"go.uber.org/zap"
)
const (
AuditActionCreate = "create"
AuditActionDelete = "delete"
AuditActionUpdate = "update"
)
func CreateUserAuditEvent(ctx context.Context, al *zap.Logger, db bun.IDB, action string, name string, id uuid.UUID, rolesBefore, rolesAfter, groupsBefore, groupsAfter []uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s %sd", name, action),
Meta: map[string]string{
"username": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("user.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
cr, _, dr := diffu(rolesBefore, rolesAfter)
ncr, err := dao.GetNamesByIds(ctx, db, cr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
ndr, err := dao.GetNamesByIds(ctx, db, dr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, r := range ncr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s added to user %s", r, name),
Meta: map[string]string{
"username": name,
"roles_name": r, // TODO: add info like namespace and project
},
}
// user.role.created is user.project.created in rcloud
if err := audit.CreateV1Event(al, sd, detail, "user.role.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, r := range ndr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s deleted from user %s", r, name),
Meta: map[string]string{
"username": name,
"role_name": r,
},
}
if err := audit.CreateV1Event(al, sd, detail, "user.role.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
cg, _, dg := diffu(groupsBefore, rolesAfter)
ncg, err := dao.GetNamesByIds(ctx, db, cg, &models.Group{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
ndg, err := dao.GetNamesByIds(ctx, db, dg, &models.Group{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, g := range ncg {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s added to group %s", name, g),
Meta: map[string]string{
"username": name,
"group_name": g,
},
}
// user.role.created is user.project.created in rcloud
if err := audit.CreateV1Event(al, sd, detail, "user.group.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, g := range ndg {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s added to group %s", name, g),
Meta: map[string]string{
"username": name,
"group_name": g,
},
}
if err := audit.CreateV1Event(al, sd, detail, "user.group.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateGroupAuditEvent(ctx context.Context, al *zap.Logger, db bun.IDB, action string, name string, id uuid.UUID, usersBefore, usersAfter, rolesBefore, rolesAfter []uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Group %s %sd", name, action),
Meta: map[string]string{
"group_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("group.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
cu, _, du := diffu(usersBefore, usersAfter)
cun, err := dao.GetUserNamesByIds(ctx, db, cu, &models.KratosIdentities{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
dun, err := dao.GetUserNamesByIds(ctx, db, du, &models.KratosIdentities{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, u := range cun {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s added to group %s", u, name),
Meta: map[string]string{
"group_name": name,
"username": u,
},
}
if err := audit.CreateV1Event(al, sd, detail, "group.user.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, u := range dun {
detail := &audit.EventDetail{
Message: fmt.Sprintf("User %s deleted from group %s", u, name),
Meta: map[string]string{
"group_name": name,
"username": u,
},
}
if err := audit.CreateV1Event(al, sd, detail, "group.user.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
cr, _, dr := diffu(rolesBefore, rolesAfter)
ncr, err := dao.GetNamesByIds(ctx, db, cr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
ndr, err := dao.GetNamesByIds(ctx, db, dr, &models.Role{})
if err != nil {
_log.Warn("unable to create audit event", err)
}
for _, r := range ncr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s added to group %s", r, name),
Meta: map[string]string{
"group_name": name,
"roles_name": r, // TODO: add info like namespace and project
},
}
// group.role.created is group.project.created in rcloud
if err := audit.CreateV1Event(al, sd, detail, "group.role.created", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
for _, r := range ndr {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s deleted from group %s", r, name),
Meta: map[string]string{
"group_name": name,
"role_name": r,
},
}
if err := audit.CreateV1Event(al, sd, detail, "group.role.deleted", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateRoleAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID, permissions []string) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Role %s %sd", name, action),
Meta: map[string]string{
"role_name": name,
"permissions": strings.Join(permissions, ","), // TODO: Should we split it into individual ones?
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("role.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateProjectAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Project %s %sd", name, action),
Meta: map[string]string{
"project_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("project.%s.success", action), id.String()); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreatePartnerAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Partner %s %sd", name, action),
Meta: map[string]string{
"partner_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("partner.%s.success", action), id.String()); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateOrganizationAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID, settingsBefore, settingsAfter *systemv3.OrganizationSettings) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Organization %s %sd", name, action),
Meta: map[string]string{
"organization_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("organization.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
if settingsBefore == nil && settingsAfter == nil {
return
}
bavail := settingsBefore != nil && settingsAfter != nil
if !bavail || settingsBefore.IdleLogoutMin != settingsAfter.IdleLogoutMin {
detail := &audit.EventDetail{
Message: fmt.Sprintf("Idel logout settings updated for organization %s", name),
Meta: map[string]string{
"organization_name": name,
},
}
if settingsAfter != nil {
detail.Meta = map[string]string{
"organization_name": name,
"idle_logout_min": string(settingsAfter.IdleLogoutMin),
}
}
if err := audit.CreateV1Event(al, sd, detail, "organization.idle.timeout.settings.updated", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
bavail = bavail && settingsBefore.Lockout != nil && settingsAfter.Lockout != nil
if !bavail ||
settingsBefore.Lockout.Enabled != settingsAfter.Lockout.Enabled ||
settingsBefore.Lockout.PeriodMin != settingsAfter.Lockout.PeriodMin ||
settingsBefore.Lockout.Attempts != settingsAfter.Lockout.Attempts {
enabled := "false"
detail := &audit.EventDetail{
Message: fmt.Sprintf("Lockout settings updated for organization %s", name),
Meta: map[string]string{
"organization_name": name,
},
}
if settingsAfter != nil && settingsAfter.Lockout != nil {
if settingsAfter.Lockout.Enabled {
enabled = "true"
}
detail.Meta = map[string]string{
"organization_name": name,
"lockout_enabled": enabled,
"lockout_period_min": string(settingsAfter.Lockout.PeriodMin),
"lockout_attempts": string(settingsAfter.Lockout.Attempts),
}
}
if err := audit.CreateV1Event(al, sd, detail, "organization.lockout.settings.updated", ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
}
func CreateIdpAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Idp %s %sd", name, action),
Meta: map[string]string{
"idp_name": name,
},
}
// idp.create.success is idp.config.created in rcloud
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("idp.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateOidcAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Oidc %s %sd", name, action),
Meta: map[string]string{
"oidc_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("oidc.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateApiKeyAuditEvent(ctx context.Context, al *zap.Logger, action string, id string) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("ApiKey %s %sd", id, action),
Meta: map[string]string{
"apikey": id,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("apikey.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
func CreateClusterAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Cluster %s %sd", name, action),
Meta: map[string]string{
"cluster_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("cluster.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
// TODO: figure out how this is to be added
func CreateLocationAuditEvent(ctx context.Context, al *zap.Logger, action string, name string, id uuid.UUID) {
sd, ok := GetSessionDataFromContext(ctx)
if !ok {
_log.Warn("unable to create audit event: could not fetch info from context")
return
}
detail := &audit.EventDetail{
Message: fmt.Sprintf("Location %s %sd", name, action),
Meta: map[string]string{
"location_name": name,
},
}
if err := audit.CreateV1Event(al, sd, detail, fmt.Sprintf("location.%s.success", action), ""); err != nil {
_log.Warn("unable to create audit event", err)
}
}
+30 -21
View File
@@ -33,6 +33,7 @@ import (
"github.com/spf13/viper"
bun "github.com/uptrace/bun"
"github.com/uptrace/bun/driver/pgdriver"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -93,14 +94,15 @@ type clusterService struct {
downloadData common.DownloadData
clusterHandlers []event.Handler
bs BootstrapService
al *zap.Logger
}
// NewClusterService return new cluster service
func NewClusterService(db *bun.DB, data *common.DownloadData, bs BootstrapService) ClusterService {
return &clusterService{db: db, downloadData: *data, bs: bs}
func NewClusterService(db *bun.DB, data *common.DownloadData, bs BootstrapService, al *zap.Logger) ClusterService {
return &clusterService{db: db, downloadData: *data, bs: bs, al: al}
}
func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster) (*infrav3.Cluster, error) {
func (s *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster) (*infrav3.Cluster, error) {
var errormsg string
if cluster.Metadata.Project == "" {
cluster.Status = &commonv3.Status{
@@ -112,7 +114,7 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
}
var proj models.Project
_, err := dao.GetByName(ctx, es.db, cluster.Metadata.Project, &proj)
_, err := dao.GetByName(ctx, s.db, cluster.Metadata.Project, &proj)
if err != nil {
return &infrav3.Cluster{}, err
}
@@ -155,7 +157,7 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
return cluster, fmt.Errorf(errormsg)
}
clusterPresent, err := dao.GetByNamePartnerOrg(ctx, es.db, cluster.Metadata.Name, uuid.NullUUID{UUID: proj.PartnerId, Valid: true},
clusterPresent, err := dao.GetByNamePartnerOrg(ctx, s.db, cluster.Metadata.Name, uuid.NullUUID{UUID: proj.PartnerId, Valid: true},
uuid.NullUUID{UUID: proj.OrganizationId, Valid: true}, &models.Cluster{})
if err != nil && err.Error() == "sql: no rows in result set" {
_log.Infof("Skipping as first time cluster create ")
@@ -166,7 +168,7 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
metro := &models.Metro{}
if cluster.Spec.Metro != nil && cluster.Spec.Metro.Name != "" {
if mdb, err := dao.GetByNamePartnerOrg(ctx, es.db, cluster.Spec.Metro.Name, uuid.NullUUID{UUID: proj.PartnerId, Valid: true}, uuid.NullUUID{UUID: uuid.Nil, Valid: false}, metro); err != nil {
if mdb, err := dao.GetByNamePartnerOrg(ctx, s.db, cluster.Spec.Metro.Name, uuid.NullUUID{UUID: proj.PartnerId, Valid: true}, uuid.NullUUID{UUID: uuid.Nil, Valid: false}, metro); err != nil {
errormsg = "Invalid cluster location, provide a valid metro name"
cluster.Status = &commonv3.Status{
ConditionType: "Create",
@@ -235,7 +237,7 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
cluster.Spec.ClusterData.Health = infrav3.Health_EDGE_IGNORE
tx, err := es.db.BeginTx(ctx, &sql.TxOptions{})
tx, err := s.db.BeginTx(ctx, &sql.TxOptions{})
if err != nil {
return &infrav3.Cluster{}, err
}
@@ -263,10 +265,10 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
}
_log.Infow("Created the cluster: ", "Cluster", edb)
clusterResp := es.prepareClusterResponse(ctx, cluster, edb, metro, pcList, true)
clusterResp := s.prepareClusterResponse(ctx, cluster, edb, metro, pcList, true)
if clusterGeneration == constants.Cluster_V2 && edb.PartnerId != uuid.Nil && edb.OrganizationId != uuid.Nil {
operatorSpecStr, err := clstrutil.GetClusterOperatorYaml(ctx, &es.downloadData, clusterResp)
operatorSpecStr, err := clstrutil.GetClusterOperatorYaml(ctx, &s.downloadData, clusterResp)
if err != nil {
_log.Errorw("Error downloading v2 cluster operator yaml", "Error", err)
return &infrav3.Cluster{}, err
@@ -305,10 +307,11 @@ func (es *clusterService) Create(ctx context.Context, cluster *infrav3.Cluster)
ID: edb.ID.String(),
}
for _, h := range es.clusterHandlers {
for _, h := range s.clusterHandlers {
h.OnChange(ev)
}
CreateClusterAuditEvent(ctx, s.al, AuditActionCreate, clusterResp.GetMetadata().GetName(), edb.ID)
return clusterResp, nil
}
@@ -472,7 +475,7 @@ func (s *clusterService) prepareClusterResponse(ctx context.Context, clstr *infr
return clstr
}
func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster) (*infrav3.Cluster, error) {
func (s *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster) (*infrav3.Cluster, error) {
var errormsg string
@@ -485,7 +488,7 @@ func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster)
return cluster, fmt.Errorf("invalid cluster data, name is missing")
}
edb, err := dao.GetByName(ctx, cs.db, cluster.Metadata.Name, &models.Cluster{})
edb, err := dao.GetByName(ctx, s.db, cluster.Metadata.Name, &models.Cluster{})
if err != nil {
return &infrav3.Cluster{}, fmt.Errorf(errormsg)
}
@@ -532,7 +535,7 @@ func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster)
if cluster.Spec.Metro != nil && cdb.MetroId.String() != cluster.Spec.Metro.Id {
metro := &models.Metro{}
if cluster.Spec.Metro.Name != "" {
if mdb, err := dao.GetByNamePartnerOrg(ctx, cs.db, cluster.Spec.Metro.Name, uuid.NullUUID{UUID: pid, Valid: true}, uuid.NullUUID{UUID: uuid.Nil, Valid: false}, metro); err != nil {
if mdb, err := dao.GetByNamePartnerOrg(ctx, s.db, cluster.Spec.Metro.Name, uuid.NullUUID{UUID: pid, Valid: true}, uuid.NullUUID{UUID: uuid.Nil, Valid: false}, metro); err != nil {
errormsg = "Invalid cluster location, provide a valid metro name"
cluster.Status = &commonv3.Status{
ConditionType: "Update",
@@ -567,12 +570,12 @@ func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster)
}
}
err = cdao.UpdateCluster(ctx, cs.db, cdb)
err = cdao.UpdateCluster(ctx, s.db, cdb)
if err != nil {
return &infrav3.Cluster{}, err
}
cs.notifyCluster(ctx, cluster)
s.notifyCluster(ctx, cluster)
ev := event.Resource{
PartnerID: cluster.Metadata.Partner,
@@ -583,19 +586,21 @@ func (cs *clusterService) Update(ctx context.Context, cluster *infrav3.Cluster)
ID: cluster.Metadata.Id,
}
for _, h := range cs.clusterHandlers {
for _, h := range s.clusterHandlers {
h.OnChange(ev)
}
/*for _, h := range s.placementHandlers {
h.OnChange(ev)
}*/
CreateClusterAuditEvent(ctx, s.al, AuditActionUpdate, cluster.GetMetadata().GetName(), cdb.ID)
return cluster, nil
}
func (cs *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster) error {
func (s *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster) error {
cluster, err := cs.Get(ctx, func(qo *commonv3.QueryOptions) {
cluster, err := s.Get(ctx, func(qo *commonv3.QueryOptions) {
qo.Name = cluster.Metadata.Name
qo.Project = cluster.Metadata.Project
qo.Extended = true
@@ -606,7 +611,7 @@ func (cs *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster)
clusterId := cluster.Metadata.Id
projectId := cluster.Metadata.Project
err = cs.deleteBootstrapAgentForCluster(ctx, cluster)
err = s.deleteBootstrapAgentForCluster(ctx, cluster)
if err != nil {
return err
}
@@ -616,7 +621,7 @@ func (cs *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster)
_log.Debugw("setting cluster condition to pending delete", "name", cluster.Metadata.Name, "conditions", cluster.Spec.ClusterData.ClusterStatus.Conditions)
clstrutil.SetClusterCondition(cluster, clstrutil.NewClusterDelete(constants.Pending, "deleted"))
err = cs.UpdateClusterConditionStatus(ctx, cluster)
err = s.UpdateClusterConditionStatus(ctx, cluster)
if err != nil {
return errors.Wrapf(err, "could not update cluster %s status to pending delete", cluster.Metadata.Name)
}
@@ -630,10 +635,14 @@ func (cs *clusterService) Delete(ctx context.Context, cluster *infrav3.Cluster)
ID: clusterId,
}
for _, h := range cs.clusterHandlers {
for _, h := range s.clusterHandlers {
h.OnChange(ev)
}
id, err := uuid.Parse(clusterId)
if err == nil {
CreateClusterAuditEvent(ctx, s.al, AuditActionDelete, cluster.GetMetadata().GetName(), id)
}
return nil
}
+5 -5
View File
@@ -30,7 +30,7 @@ func TestCreateCluster(t *testing.T) {
RelayAgentImage: "rafaysystems/relay:latest",
}
ps := NewClusterService(db, downloadData, NewBootstrapService(db))
ps := NewClusterService(db, downloadData, NewBootstrapService(db), getLogger())
puuid := uuid.New().String()
cuuid := uuid.New().String()
@@ -74,7 +74,7 @@ func TestUpdateCluster(t *testing.T) {
RelayAgentImage: "rafaysystems/relay:latest",
}
ps := NewClusterService(db, downloadData, NewBootstrapService(db))
ps := NewClusterService(db, downloadData, NewBootstrapService(db), getLogger())
puuid := uuid.New().String()
cuuid := uuid.New().String()
@@ -108,7 +108,7 @@ func TestSelectCluster(t *testing.T) {
RelayAgentImage: "rafaysystems/relay:latest",
}
ps := NewClusterService(db, downloadData, NewBootstrapService(db))
ps := NewClusterService(db, downloadData, NewBootstrapService(db), getLogger())
puuid := uuid.New().String()
cuuid := uuid.New().String()
@@ -142,7 +142,7 @@ func TestGetCluster(t *testing.T) {
RelayAgentImage: "rafaysystems/relay:latest",
}
ps := NewClusterService(db, downloadData, NewBootstrapService(db))
ps := NewClusterService(db, downloadData, NewBootstrapService(db), getLogger())
puuid := uuid.New().String()
cuuid := uuid.New().String()
@@ -175,7 +175,7 @@ func TestListCluster(t *testing.T) {
RelayAgentImage: "rafaysystems/relay:latest",
}
ps := NewClusterService(db, downloadData, NewBootstrapService(db))
ps := NewClusterService(db, downloadData, NewBootstrapService(db), getLogger())
puuid := uuid.New().String()
ouuid := uuid.New().String()
+77 -48
View File
@@ -16,6 +16,7 @@ import (
userv3 "github.com/RafayLabs/rcloud-base/proto/types/userpb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -44,49 +45,66 @@ type GroupService interface {
type groupService struct {
db *bun.DB
azc AuthzService
al *zap.Logger
}
// NewGroupService return new group service
func NewGroupService(db *bun.DB, azc AuthzService) GroupService {
return &groupService{db: db, azc: azc}
func NewGroupService(db *bun.DB, azc AuthzService, al *zap.Logger) GroupService {
return &groupService{db: db, azc: azc, al: al}
}
func (s *groupService) deleteGroupRoleRelaitons(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
// delete previous entries
// deleteGroupRoleRelaitons deletes existing group-role relations
func (s *groupService) deleteGroupRoleRelaitons(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
// TODO: single delete command
err := dao.DeleteX(ctx, db, "group_id", groupId, &models.GroupRole{})
ids := []uuid.UUID{}
gr := []models.GroupRole{}
err := dao.DeleteXR(ctx, db, "group_id", groupId, &gr)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
err = dao.DeleteX(ctx, db, "group_id", groupId, &models.ProjectGroupRole{})
if err != nil {
return &userv3.Group{}, err
for _, r := range gr {
ids = append(ids, r.RoleId)
}
err = dao.DeleteX(ctx, db, "group_id", groupId, &models.ProjectGroupNamespaceRole{})
pgr := []models.ProjectGroupRole{}
err = dao.DeleteXR(ctx, db, "group_id", groupId, &pgr)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
for _, r := range pgr {
ids = append(ids, r.RoleId)
}
pgnr := []models.ProjectGroupNamespaceRole{}
err = dao.DeleteXR(ctx, db, "group_id", groupId, &pgnr)
if err != nil {
return &userv3.Group{}, nil, err
}
for _, r := range pgnr {
ids = append(ids, r.RoleId)
}
_, err = s.azc.DeletePolicies(ctx, &authzv1.Policy{Sub: "g:" + group.GetMetadata().GetName()})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete group-role relations from authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to delete group-role relations from authz; %v", err)
}
return group, nil
return group, ids, nil
}
// Map roles to groups
func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB, group *userv3.Group, ids parsedIds) (*userv3.Group, error) {
// TODO: add transactions
func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB, group *userv3.Group, ids parsedIds) (*userv3.Group, []uuid.UUID, error) {
projectNamespaceRoles := group.GetSpec().GetProjectNamespaceRoles()
var pgrs []models.ProjectGroupRole
var grs []models.GroupRole
var ps []*authzv1.Policy
var rids []uuid.UUID
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.Group{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
@@ -94,9 +112,10 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
rids = append(rids, rle.ID)
scope = strings.ToLower(rle.Scope)
} else {
return &userv3.Group{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.Group{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
@@ -122,7 +141,7 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
})
case "organization":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
gr := models.GroupRole{
Trash: false,
@@ -142,14 +161,14 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
})
case "project":
if org == "" {
return &userv3.Group{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.Group{}, fmt.Errorf("no project name provided for role '%v'", roleName)
return &userv3.Group{}, nil, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := dao.GetProjectId(ctx, s.db, project)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find project '%v'", project)
return &userv3.Group{}, nil, fmt.Errorf("unable to find project '%v'", project)
}
pgr := models.ProjectGroupRole{
Trash: false,
@@ -174,49 +193,55 @@ func (s *groupService) createGroupRoleRelations(ctx context.Context, db bun.IDB,
if len(pgrs) > 0 {
_, err := dao.Create(ctx, db, &pgrs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
}
if len(grs) > 0 {
_, err := dao.Create(ctx, db, &grs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
}
if len(ps) > 0 {
success, err := s.azc.CreatePolicies(ctx, &authzv1.Policies{Policies: ps})
if err != nil || !success.Res {
return &userv3.Group{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
}
return group, nil
return group, rids, nil
}
func (s *groupService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
err := dao.DeleteX(ctx, db, "group_id", groupId, &models.GroupAccount{})
func (s *groupService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
ga := []models.GroupAccount{}
err := dao.DeleteXR(ctx, db, "group_id", groupId, &ga)
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete user; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to remove user from group user; %v", err)
}
_, err = s.azc.DeleteUserGroups(ctx, &authzv1.UserGroup{Grp: "g:" + group.GetMetadata().GetName()})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
}
return group, nil
ids := []uuid.UUID{}
for _, r := range ga {
ids = append(ids, r.AccountId)
}
return group, ids, nil
}
// Update the users(account) mapped to each group
func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, error) {
// TODO: add transactions
func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.IDB, groupId uuid.UUID, group *userv3.Group) (*userv3.Group, []uuid.UUID, error) {
var grpaccs []models.GroupAccount
var ugs []*authzv1.UserGroup
var uids []uuid.UUID
for _, account := range unique(group.GetSpec().GetUsers()) {
// FIXME: do combined lookup
entity, err := dao.GetIdByTraits(ctx, db, account, &models.KratosIdentities{})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to find user '%v'", account)
return &userv3.Group{}, nil, fmt.Errorf("unable to find user '%v'", account)
}
if acc, ok := entity.(*models.KratosIdentities); ok {
grp := models.GroupAccount{
@@ -227,6 +252,7 @@ func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.I
GroupId: groupId,
Active: true,
}
uids = append(uids, acc.ID)
grpaccs = append(grpaccs, grp)
ugs = append(ugs, &authzv1.UserGroup{
Grp: "g:" + group.GetMetadata().GetName(),
@@ -235,21 +261,19 @@ func (s *groupService) createGroupAccountRelations(ctx context.Context, db bun.I
}
}
if len(grpaccs) == 0 {
return group, nil
return group, nil, nil
}
_, err := dao.Create(ctx, db, &grpaccs)
if err != nil {
return &userv3.Group{}, err
return &userv3.Group{}, nil, err
}
// TODO: revert our db inserts if this fails
// Just FYI, the success can be false if we delete the db directly but casbin has it available internally
_, err = s.azc.CreateUserGroups(ctx, &authzv1.UserGroups{UserGroups: ugs})
if err != nil {
return &userv3.Group{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.Group{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
return group, nil
return group, uids, nil
}
// TODO: move this to utils, make it accept two strings (names)
@@ -303,13 +327,13 @@ func (s *groupService) Create(ctx context.Context, group *userv3.Group) (*userv3
//update v3 spec
if grp, ok := entity.(*models.Group); ok {
// we can get previous group using the id, find users/roles from that and delete those
group, err = s.createGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersAfter, err := s.createGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
group, rolesAfter, err := s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -320,9 +344,10 @@ func (s *groupService) Create(ctx context.Context, group *userv3.Group) (*userv3
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateGroupAuditEvent(ctx, s.al, s.db, AuditActionCreate, group.GetMetadata().GetName(), grp.ID, []uuid.UUID{}, usersAfter, []uuid.UUID{}, rolesAfter)
return group, nil
}
tx.Rollback()
return &userv3.Group{}, fmt.Errorf("unable to create group")
}
@@ -423,22 +448,22 @@ func (s *groupService) Update(ctx context.Context, group *userv3.Group) (*userv3
}
// update account/role links
group, err = s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersBefore, err := s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersAfter, err := s.createGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
group, rolesBefore, err := s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
group, rolesAfter, err := s.createGroupRoleRelations(ctx, tx, group, parsedIds{Id: grp.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -462,6 +487,8 @@ func (s *groupService) Update(ctx context.Context, group *userv3.Group) (*userv3
Users: group.Spec.Users, // TODO: update from db resp or no update?
ProjectNamespaceRoles: group.Spec.ProjectNamespaceRoles,
}
CreateGroupAuditEvent(ctx, s.al, s.db, AuditActionUpdate, group.GetMetadata().GetName(), grp.ID, usersBefore, usersAfter, rolesBefore, rolesAfter)
}
return group, nil
@@ -484,12 +511,12 @@ func (s *groupService) Delete(ctx context.Context, group *userv3.Group) (*userv3
return &userv3.Group{}, err
}
group, err = s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
group, rolesBefore, err := s.deleteGroupRoleRelaitons(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
}
group, err = s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
group, usersBefore, err := s.deleteGroupAccountRelations(ctx, tx, grp.ID, group)
if err != nil {
tx.Rollback()
return &userv3.Group{}, err
@@ -505,6 +532,8 @@ func (s *groupService) Delete(ctx context.Context, group *userv3.Group) (*userv3
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateGroupAuditEvent(ctx, s.al, s.db, AuditActionDelete, group.GetMetadata().GetName(), grp.ID, usersBefore, []uuid.UUID{}, rolesBefore, []uuid.UUID{})
return group, nil
}
+60 -162
View File
@@ -84,16 +84,11 @@ func TestCreateGroupNoUsersNoRoles(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group" WHERE .organization_id = '` + ouuid + `'. AND .partner_id = '` + puuid + `'. AND .name = 'group-` + guuid + `'.`).
WillReturnError(fmt.Errorf("no data available"))
@@ -120,30 +115,22 @@ func TestCreateGroupDuplicate(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
// Try to recreate
addFetchExpectation(mock, "group")
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO "authsrv_group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
mock.ExpectCommit()
group := &userv3.Group{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "group-" + guuid},
Spec: &userv3.GroupSpec{},
}
// Try to recreate
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectBegin()
// TODO: more precise checks
mock.ExpectQuery(`INSERT INTO "authsrv_group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
mock.ExpectCommit()
_, err := gs.Create(context.Background(), group)
if err == nil {
t.Fatal("should not be able to recreate group with same name")
@@ -155,10 +142,10 @@ func TestCreateGroupDuplicate(t *testing.T) {
func TestCreateGroupWithUsersNoRoles(t *testing.T) {
tt := []struct {
name string
users []string
users int
}{
{"single user", []string{"users-" + uuid.New().String()}},
{"multiple users", []string{"users-" + uuid.New().String(), "users-" + uuid.New().String()}},
{"single user", 1},
{"multiple users", 2},
}
for _, tc := range tt {
t.Run(tc.name, func(t *testing.T) {
@@ -166,26 +153,22 @@ func TestCreateGroupWithUsersNoRoles(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
// TODO: more precise checks
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group" WHERE .organization_id = '` + ouuid + `'. AND .partner_id = '` + puuid + `'. AND .name = 'group-` + guuid + `'.`).
WillReturnError(fmt.Errorf("no data available"))
puuid, ouuid := addParterOrgFetchExpectation(mock)
addUnavailableExpectation(mock, "group", puuid, ouuid, guuid)
mock.ExpectBegin()
mock.ExpectQuery(`INSERT INTO "authsrv_group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
for _, u := range tc.users {
mock.ExpectQuery(`SELECT "identities"."id" FROM "identities" WHERE .*traits ->> 'email' = '` + u + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuid.New().String(), []byte(`{"email":"johndoe@provider.com"}`)))
users := []string{}
var i int
for i = 0; i < tc.users; i++ {
user := addUserFetchExpectation(mock)
users = append(users, "user-"+user)
}
mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
@@ -193,7 +176,7 @@ func TestCreateGroupWithUsersNoRoles(t *testing.T) {
group := &userv3.Group{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "group-" + guuid},
Spec: &userv3.GroupSpec{Users: tc.users},
Spec: &userv3.GroupSpec{Users: users},
}
group, err := gs.Create(context.Background(), group)
if err != nil {
@@ -201,19 +184,17 @@ func TestCreateGroupWithUsersNoRoles(t *testing.T) {
}
performGroupBasicChecks(t, group, guuid)
for i, ru := range group.Spec.Users {
if ru != tc.users[i] {
t.Errorf("user id '%v' not found in resource response", tc.users[i])
if ru != users[i] {
t.Errorf("user id '%v' not found in resource response", users[i])
}
}
performBasicAuthzChecks(t, mazc, 0, 0, 1, 0, 0, 0)
performGroupBasicAuthzChecks(t, mazc, guuid, tc.users, []*userv3.ProjectNamespaceRole{})
performGroupBasicAuthzChecks(t, mazc, guuid, users, []*userv3.ProjectNamespaceRole{})
})
}
}
func TestCreateGroupNoUsersWithRoles(t *testing.T) {
// projectid := uuid.New().String()
// var namespaceid int64 = 7
tt := []struct {
name string
roles []*userv3.ProjectNamespaceRole
@@ -234,18 +215,12 @@ func TestCreateGroupNoUsersWithRoles(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
pruuid := uuid.New().String()
// TODO: more precise checks
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group" WHERE .organization_id = '` + ouuid + `'. AND .partner_id = '` + puuid + `'. AND .name = 'group-` + guuid + `'.`).
WillReturnError(fmt.Errorf("no data available"))
@@ -314,17 +289,12 @@ func TestCreateGroupWithUsersWithRoles(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group" WHERE .organization_id = '` + ouuid + `'. AND .partner_id = '` + puuid + `'. AND .name = 'group-` + guuid + `'.`).WithArgs()
mock.ExpectBegin()
@@ -397,36 +367,27 @@ func TestUpdateGroupWithUsersWithRoles(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
pruuid := uuid.New().String()
// performing update
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id", "group"."name",.* FROM "authsrv_group" AS "group" WHERE .*name = 'group-` + guuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(guuid, "group-"+guuid))
mock.ExpectBegin()
mock.ExpectExec(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
addGroupUserMappingsUpdateExpectation(mock, guuid)
for _, u := range tc.users {
// addUserFetchExpectation(mock) // TODO: look into this
mock.ExpectQuery(`SELECT "identities"."id" FROM "identities" WHERE .*traits ->> 'email' = '` + u + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuid.New().String(), []byte(`{"email":"johndoe@provider.com"}`)))
}
mock.ExpectQuery(`INSERT INTO "authsrv_groupaccount"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectExec(`UPDATE "authsrv_grouprole" AS "grouprole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectgrouprole" AS "projectgrouprole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
addGroupRoleMappingsUpdateExpectation(mock, guuid)
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
@@ -470,30 +431,14 @@ func TestGroupDelete(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "group"."id", "group"."name", .* FROM "authsrv_group" AS "group" WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(guuid, "group-"+guuid))
gs := NewGroupService(db, &mazc, getLogger())
puuid, ouuid := addParterOrgFetchExpectation(mock)
guuid := addFetchExpectation(mock, "group")
mock.ExpectBegin()
mock.ExpectExec(`UPDATE "authsrv_grouprole" AS "grouprole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectgrouprole" AS "projectgrouprole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE ."group_id" = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_group" AS "group" SET trash = TRUE WHERE .id = '` + guuid).
WillReturnResult(sqlmock.NewResult(1, 1))
addGroupRoleMappingsUpdateExpectation(mock, guuid)
addGroupUserMappingsUpdateExpectation(mock, guuid)
addDeleteExpectation(mock, "group", guuid)
mock.ExpectCommit()
group := &userv3.Group{
@@ -510,14 +455,13 @@ func TestGroupDeleteNonExist(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "group"."id", "group"."name", .* FROM "authsrv_group" AS "group" WHERE`).
WithArgs().WillReturnError(fmt.Errorf("No data available"))
addUnavailableExpectation(mock, "group", puuid, ouuid, guuid)
group := &userv3.Group{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "group-" + guuid},
@@ -533,30 +477,16 @@ func TestGroupGetByName(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
uuuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "group"."id", "group"."name", .* FROM "authsrv_group" AS "group" WHERE .*name = 'group-` + guuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(guuid, "group-"+guuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
addFetchByNameExpectation(mock, "group", guuid)
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" JOIN authsrv_groupaccount ON identities.id=authsrv_groupaccount.account_id WHERE .authsrv_groupaccount.group_id = '` + guuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE .authsrv_grouprole.group_id = '` + guuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
addGroupRoleMappingsFetchExpectation(mock, guuid, puuid)
group := &userv3.Group{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "group-" + guuid},
@@ -573,8 +503,8 @@ func TestGroupGetByName(t *testing.T) {
if len(group.GetSpec().GetProjectNamespaceRoles()) != 3 {
t.Errorf("invalid number of roles returned for user, expected 3; got '%v'", len(group.GetSpec().GetProjectNamespaceRoles()))
}
if group.GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 9 {
t.Errorf("invalid namespace in role returned for user, expected 9; got '%v'", group.GetSpec().GetProjectNamespaceRoles()[2].Namespace)
if group.GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 7 {
t.Errorf("invalid namespace in role returned for user, expected 7; got '%v'", group.GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
}
@@ -583,7 +513,7 @@ func TestGroupGetById(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid := uuid.New().String()
puuid := uuid.New().String()
@@ -629,41 +559,25 @@ func TestGroupList(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid1 := uuid.New().String()
guuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
uuuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
_, _ = addOrgParterFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id", "group"."name", .* FROM "authsrv_group" AS "group"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(guuid1, "group-"+guuid1).AddRow(guuid2, "group-"+guuid2))
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" JOIN authsrv_groupaccount ON identities.id=authsrv_groupaccount.account_id WHERE .authsrv_groupaccount.group_id = '` + guuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
addGroupRoleMappingsFetchExpectation(mock, guuid1, pruuid)
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" JOIN authsrv_groupaccount ON identities.id=authsrv_groupaccount.account_id WHERE .authsrv_groupaccount.group_id = '` + guuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
addGroupRoleMappingsFetchExpectation(mock, guuid2, pruuid)
qo := &commonv3.QueryOptions{}
grouplist, err := gs.List(context.Background(), query.WithOptions(qo))
@@ -686,41 +600,25 @@ func TestGroupListFiltered(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewGroupService(db, &mazc)
gs := NewGroupService(db, &mazc, getLogger())
guuid1 := uuid.New().String()
guuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
uuuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
puuid, ouuid := addOrgParterFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id", "group"."name", .*WHERE .name ILIKE '%filter-query%'. AND .partner_id = '` + puuid + `'. AND .organization_id = '` + ouuid + `'. ORDER BY "email" asc LIMIT 50 OFFSET 20`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).
AddRow(guuid1, "group-"+guuid1).AddRow(guuid2, "group-"+guuid2))
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" JOIN authsrv_groupaccount ON identities.id=authsrv_groupaccount.account_id WHERE .authsrv_groupaccount.group_id = '` + guuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
addGroupRoleMappingsFetchExpectation(mock, guuid1, pruuid)
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" JOIN authsrv_groupaccount ON identities.id=authsrv_groupaccount.account_id WHERE .authsrv_groupaccount.group_id = '` + guuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgroupnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgroupnamespacerole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgroupnamespacerole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
addGroupRoleMappingsFetchExpectation(mock, guuid2, pruuid)
qo := &commonv3.QueryOptions{Q: "filter-query", Limit: 50, Offset: 20, OrderBy: "email", Order: "asc"}
grouplist, err := gs.List(context.Background(), query.WithOptions(qo))
+11 -2
View File
@@ -20,6 +20,7 @@ import (
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
"github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
@@ -36,10 +37,11 @@ type IdpService interface {
type idpService struct {
db *bun.DB
appHost string
al *zap.Logger
}
func NewIdpService(db *bun.DB, hostUrl string) IdpService {
return &idpService{db: db, appHost: hostUrl}
func NewIdpService(db *bun.DB, hostUrl string, al *zap.Logger) IdpService {
return &idpService{db: db, appHost: hostUrl, al: al}
}
func generateAcsURL(id string, hostUrl string) string {
@@ -205,6 +207,9 @@ func (s *idpService) Create(ctx context.Context, idp *systemv3.Idp) (*systemv3.I
SpEntityId: acsURL,
},
}
CreateIdpAuditEvent(ctx, s.al, AuditActionCreate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -381,6 +386,8 @@ func (s *idpService) Update(ctx context.Context, idp *systemv3.Idp) (*systemv3.I
SpEntityId: acsURL,
},
}
CreateIdpAuditEvent(ctx, s.al, AuditActionUpdate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -452,5 +459,7 @@ func (s *idpService) Delete(ctx context.Context, idp *systemv3.Idp) error {
if err != nil {
return err
}
CreateIdpAuditEvent(ctx, s.al, AuditActionDelete, name, entity.Id)
return nil
}
+10 -2
View File
@@ -14,6 +14,7 @@ import (
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
@@ -31,10 +32,11 @@ type OIDCProviderService interface {
type oidcProvider struct {
db *bun.DB
kratosUrl string
al *zap.Logger
}
func NewOIDCProviderService(db *bun.DB, kratosUrl string) OIDCProviderService {
return &oidcProvider{db: db, kratosUrl: kratosUrl}
func NewOIDCProviderService(db *bun.DB, kratosUrl string, al *zap.Logger) OIDCProviderService {
return &oidcProvider{db: db, kratosUrl: kratosUrl, al: al}
}
func generateCallbackUrl(id string, kUrl string) string {
@@ -152,6 +154,8 @@ func (s *oidcProvider) Create(ctx context.Context, provider *systemv3.OIDCProvid
CallbackUrl: generateCallbackUrl(entity.Id.String(), s.kratosUrl),
},
}
CreateOidcAuditEvent(ctx, s.al, AuditActionCreate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -374,6 +378,8 @@ func (s *oidcProvider) Update(ctx context.Context, provider *systemv3.OIDCProvid
CallbackUrl: generateCallbackUrl(provider.GetMetadata().GetId(), s.kratosUrl),
},
}
CreateOidcAuditEvent(ctx, s.al, AuditActionUpdate, rv.GetMetadata().GetName(), entity.Id)
return rv, nil
}
@@ -392,5 +398,7 @@ func (s *oidcProvider) Delete(ctx context.Context, provider *systemv3.OIDCProvid
if err != nil {
return err
}
CreateOidcAuditEvent(ctx, s.al, AuditActionDelete, name, entity.Id)
return nil
}
+17 -4
View File
@@ -12,6 +12,7 @@ import (
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -39,11 +40,12 @@ type OrganizationService interface {
// organizationService implements OrganizationService
type organizationService struct {
db *bun.DB
al *zap.Logger
}
// NewOrganizationService return new organization service
func NewOrganizationService(db *bun.DB) OrganizationService {
return &organizationService{db}
func NewOrganizationService(db *bun.DB, al *zap.Logger) OrganizationService {
return &organizationService{db, al}
}
func (s *organizationService) Create(ctx context.Context, org *systemv3.Organization) (*systemv3.Organization, error) {
@@ -99,6 +101,8 @@ func (s *organizationService) Create(ctx context.Context, org *systemv3.Organiza
if createdOrg, ok := entity.(*models.Organization); ok {
//update v3 spec
org.Metadata.Id = createdOrg.ID.String()
CreateOrganizationAuditEvent(ctx, s.al, AuditActionCreate, org.GetMetadata().GetName(), createdOrg.ID, nil, org.GetSpec().GetSettings())
}
return org, nil
@@ -195,8 +199,11 @@ func (s *organizationService) Update(ctx context.Context, organization *systemv3
}
if org, ok := entity.(*models.Organization); ok {
settingsAfter := organization.GetSpec().GetSettings()
settingsBefore := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &settingsBefore) // ignore any unmarshelling issues
sb, err := json.MarshalIndent(organization.GetSpec().GetSettings(), "", "\t")
sb, err := json.MarshalIndent(settingsAfter, "", "\t")
if err != nil {
return &systemv3.Organization{}, err
}
@@ -226,6 +233,8 @@ func (s *organizationService) Update(ctx context.Context, organization *systemv3
if err != nil {
return &systemv3.Organization{}, err
}
CreateOrganizationAuditEvent(ctx, s.al, AuditActionUpdate, organization.GetMetadata().GetName(), org.ID, &settingsBefore, settingsAfter)
}
return organization, nil
@@ -239,7 +248,7 @@ func (s *organizationService) Delete(ctx context.Context, organization *systemv3
}
if org, ok := entity.(*models.Organization); ok {
err := dao.Delete(ctx, s.db, org.ID, org)
err := dao.DeleteR(ctx, s.db, org.ID, org)
if err != nil {
return &systemv3.Organization{}, err
}
@@ -247,6 +256,10 @@ func (s *organizationService) Delete(ctx context.Context, organization *systemv3
//update v3 status
organization.Metadata.Name = org.Name
organization.Metadata.ModifiedAt = timestamppb.New(org.ModifiedAt)
orgSettings := systemv3.OrganizationSettings{}
_ = json.Unmarshal(org.Settings, &orgSettings) // ignore any unmarshelling issues
CreateOrganizationAuditEvent(ctx, s.al, AuditActionDelete, organization.GetMetadata().GetName(), org.ID, &orgSettings, nil)
}
return organization, nil
+10 -10
View File
@@ -21,7 +21,7 @@ func TestCreateOrganization(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
puuid := uuid.New().String()
ouuid := uuid.New().String()
@@ -47,7 +47,7 @@ func TestCreateOrganizationDuplicate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
gs := NewOrganizationService(db)
gs := NewOrganizationService(db, getLogger())
ouuid := uuid.New().String()
@@ -69,15 +69,15 @@ func TestOrganizationDelete(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "organization"."id", "organization"."name", .* FROM "authsrv_organization" AS "organization" WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(ouuid, "organization-"+ouuid))
mock.ExpectExec(`UPDATE "authsrv_organization"`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectQuery(`UPDATE "authsrv_organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(ouuid, "organization-"+ouuid))
organization := &systemv3.Organization{
Metadata: &v3.Metadata{Id: ouuid, Name: "organization-" + ouuid},
@@ -92,7 +92,7 @@ func TestOrganizationDeleteNonExist(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
ouuid := uuid.New().String()
@@ -104,7 +104,7 @@ func TestOrganizationDeleteNonExist(t *testing.T) {
}
_, err := ps.Delete(context.Background(), organization)
if err == nil {
t.Fatal("deleted non existant organization")
t.Fatal("deleted non existent organization")
}
}
@@ -112,7 +112,7 @@ func TestOrganizationGetByName(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
partuuid := uuid.New().String()
ouuid := uuid.New().String()
@@ -137,7 +137,7 @@ func TestOrganizationGetById(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
partuuid := uuid.New().String()
puuid := uuid.New().String()
@@ -162,7 +162,7 @@ func TestOrganizationUpdate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewOrganizationService(db)
ps := NewOrganizationService(db, getLogger())
puuid := uuid.New().String()
+10 -2
View File
@@ -11,6 +11,7 @@ import (
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -33,11 +34,12 @@ type PartnerService interface {
// partnerService implements PartnerService
type partnerService struct {
db *bun.DB
al *zap.Logger
}
// NewPartnerService return new partner service
func NewPartnerService(db *bun.DB) PartnerService {
return &partnerService{db}
func NewPartnerService(db *bun.DB, al *zap.Logger) PartnerService {
return &partnerService{db, al}
}
func (s *partnerService) Create(ctx context.Context, partner *systemv3.Partner) (*systemv3.Partner, error) {
@@ -76,6 +78,8 @@ func (s *partnerService) Create(ctx context.Context, partner *systemv3.Partner)
//update v3 spec
partner.Metadata.Id = createdPartner.ID.String()
partner.Metadata.ModifiedAt = timestamppb.New(createdPartner.ModifiedAt)
CreatePartnerAuditEvent(ctx, s.al, AuditActionCreate, partner.GetMetadata().GetName(), createdPartner.ID)
}
return partner, nil
@@ -242,6 +246,8 @@ func (s *partnerService) Update(ctx context.Context, partner *systemv3.Partner)
//update metadata and status
partner.Metadata.ModifiedAt = timestamppb.New(part.ModifiedAt)
CreatePartnerAuditEvent(ctx, s.al, AuditActionUpdate, partner.GetMetadata().GetName(), part.ID)
}
return partner, nil
@@ -258,6 +264,8 @@ func (s *partnerService) Delete(ctx context.Context, partner *systemv3.Partner)
if err != nil {
return &systemv3.Partner{}, err
}
CreatePartnerAuditEvent(ctx, s.al, AuditActionDelete, partner.GetMetadata().GetName(), part.ID)
return partner, nil
}
+8 -8
View File
@@ -37,7 +37,7 @@ func TestCreatePartner(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewPartnerService(db)
ps := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -59,7 +59,7 @@ func TestCreatePartnerDuplicate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
gs := NewPartnerService(db)
gs := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -81,7 +81,7 @@ func TestPartnerDelete(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewPartnerService(db)
ps := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -104,7 +104,7 @@ func TestPartnerDeleteNonExist(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
gs := NewPartnerService(db)
gs := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -116,7 +116,7 @@ func TestPartnerDeleteNonExist(t *testing.T) {
}
_, err := gs.Delete(context.Background(), partner)
if err == nil {
t.Fatal("deleted non existant group")
t.Fatal("deleted non existent group")
}
}
@@ -124,7 +124,7 @@ func TestPartnerGetByName(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewPartnerService(db)
ps := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -144,7 +144,7 @@ func TestPartnerGetById(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewPartnerService(db)
ps := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
@@ -165,7 +165,7 @@ func TestPartnerUpdate(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ps := NewPartnerService(db)
ps := NewPartnerService(db, getLogger())
puuid := uuid.New().String()
+12 -5
View File
@@ -8,13 +8,12 @@ import (
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
"github.com/RafayLabs/rcloud-base/pkg/common"
authzv1 "github.com/RafayLabs/rcloud-base/proto/types/authz"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
v3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
systemv3 "github.com/RafayLabs/rcloud-base/proto/types/systempb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -44,11 +43,12 @@ type ProjectService interface {
type projectService struct {
db *bun.DB
azc AuthzService
al *zap.Logger
}
// NewProjectService return new project service
func NewProjectService(db *bun.DB, azc AuthzService) ProjectService {
return &projectService{db: db, azc: azc}
func NewProjectService(db *bun.DB, azc AuthzService, al *zap.Logger) ProjectService {
return &projectService{db: db, azc: azc, al: al}
}
func (s *projectService) Create(ctx context.Context, project *systemv3.Project) (*systemv3.Project, error) {
@@ -105,6 +105,8 @@ func (s *projectService) Create(ctx context.Context, project *systemv3.Project)
project.Spec = &systemv3.ProjectSpec{
Default: createdProject.Default,
}
CreateProjectAuditEvent(ctx, s.al, AuditActionCreate, project.GetMetadata().GetName(), createdProject.ID)
}
err = tx.Commit()
if err != nil {
@@ -282,6 +284,8 @@ func (s *projectService) Update(ctx context.Context, project *systemv3.Project)
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateProjectAuditEvent(ctx, s.al, AuditActionUpdate, project.GetMetadata().GetName(), proj.ID)
}
return project, nil
@@ -324,14 +328,17 @@ func (s *projectService) Delete(ctx context.Context, project *systemv3.Project)
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
return &systemv3.Project{}, err
}
CreateProjectAuditEvent(ctx, s.al, AuditActionDelete, project.GetMetadata().GetName(), proj.ID)
}
return project, nil
}
func (s *projectService) List(ctx context.Context, project *systemv3.Project) (*systemv3.ProjectList, error) {
sd, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sd, ok := GetSessionDataFromContext(ctx)
username := ""
if !ok {
return &systemv3.ProjectList{}, fmt.Errorf("cannot perform project listing without auth")
+8 -8
View File
@@ -22,7 +22,7 @@ func TestCreateProject(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
ouuid := uuid.New().String()
@@ -50,7 +50,7 @@ func TestCreateProjectDuplicate(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
gs := NewProjectService(db, &mazc)
gs := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
@@ -73,7 +73,7 @@ func TestProjectDelete(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
@@ -101,7 +101,7 @@ func TestProjectDeleteNonExist(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
@@ -113,7 +113,7 @@ func TestProjectDeleteNonExist(t *testing.T) {
}
_, err := ps.Delete(context.Background(), project)
if err == nil {
t.Fatal("deleted non existant project")
t.Fatal("deleted non existent project")
}
}
@@ -122,7 +122,7 @@ func TestProjectGetByName(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
partuuid := uuid.New().String()
ouuid := uuid.New().String()
@@ -166,7 +166,7 @@ func TestProjectGetById(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
@@ -188,7 +188,7 @@ func TestProjectUpdate(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
ps := NewProjectService(db, &mazc)
ps := NewProjectService(db, &mazc, getLogger())
puuid := uuid.New().String()
+20 -11
View File
@@ -14,6 +14,7 @@ import (
rolev3 "github.com/RafayLabs/rcloud-base/proto/types/rolepb/v3"
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
)
@@ -42,11 +43,12 @@ type RoleService interface {
type roleService struct {
db *bun.DB
azc AuthzService
al *zap.Logger
}
// NewRoleService return new role service
func NewRoleService(db *bun.DB, azc AuthzService) RoleService {
return &roleService{db: db, azc: azc}
func NewRoleService(db *bun.DB, azc AuthzService, al *zap.Logger) RoleService {
return &roleService{db: db, azc: azc, al: al}
}
func (s *roleService) getPartnerOrganization(ctx context.Context, db bun.IDB, role *rolev3.Role) (uuid.UUID, uuid.UUID, error) {
@@ -166,17 +168,20 @@ func (s *roleService) Create(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
return &rolev3.Role{}, err
}
} else {
tx.Rollback()
return &rolev3.Role{}, fmt.Errorf("unable to create role '%v'", role.GetMetadata().GetName())
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, s.al, AuditActionCreate, role.GetMetadata().GetName(), createdRole.ID, role.GetSpec().GetRolepermissions())
return role, nil
}
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
return role, nil
tx.Rollback()
return &rolev3.Role{}, fmt.Errorf("unable to create role '%v'", role.GetMetadata().GetName())
}
@@ -279,6 +284,8 @@ func (s *roleService) Update(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, s.al, AuditActionUpdate, role.GetMetadata().GetName(), rle.ID, role.GetSpec().GetRolepermissions())
return role, nil
}
return &rolev3.Role{}, fmt.Errorf("unable to update role '%v'", role.GetMetadata().GetName())
@@ -321,6 +328,8 @@ func (s *roleService) Delete(ctx context.Context, role *rolev3.Role) (*rolev3.Ro
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateRoleAuditEvent(ctx, s.al, AuditActionDelete, role.GetMetadata().GetName(), rle.ID, []string{})
return role, nil
}
+10 -10
View File
@@ -52,7 +52,7 @@ func TestCreateRole(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -87,7 +87,7 @@ func TestCreateRoleWithPermissions(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -126,7 +126,7 @@ func TestCreateRoleDuplicate(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -160,7 +160,7 @@ func TestUpdateRole(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -201,7 +201,7 @@ func TestRoleDelete(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -234,7 +234,7 @@ func TestRoleDeleteNonExist(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
puuid := uuid.New().String()
@@ -248,7 +248,7 @@ func TestRoleDeleteNonExist(t *testing.T) {
}
_, err := rs.Delete(context.Background(), role)
if err == nil {
t.Error("deleted non existant role")
t.Error("deleted non existent role")
}
}
@@ -258,7 +258,7 @@ func TestRoleGetByName(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
rruuid := uuid.New().String()
@@ -289,7 +289,7 @@ func TestRoleGetById(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid := uuid.New().String()
rruuid := uuid.New().String()
@@ -316,7 +316,7 @@ func TestRoleList(t *testing.T) {
defer db.Close()
mazc := mockAuthzClient{}
rs := NewRoleService(db, &mazc)
rs := NewRoleService(db, &mazc, getLogger())
ruuid1 := uuid.New().String()
ruuid2 := uuid.New().String()
+167 -1
View File
@@ -1,6 +1,24 @@
package service
import "testing"
import (
"fmt"
"testing"
"github.com/DATA-DOG/go-sqlmock"
"github.com/RafayLabs/rcloud-base/pkg/audit"
"github.com/google/uuid"
"go.uber.org/zap"
)
func getLogger() *zap.Logger {
ao := audit.AuditOptions{
LogPath: "stdout",
MaxSizeMB: 1,
MaxBackups: 10, // Should we let sidecar do rotation?
MaxAgeDays: 10, // Make these configurable via env
}
return audit.GetAuditLogger(&ao)
}
func performBasicAuthzChecks(t *testing.T, mazc mockAuthzClient, cpCount, dpCount, cugCount, dugCount, crpmCount, drpmCount int) {
if len(mazc.cp) != cpCount {
@@ -37,3 +55,151 @@ func performBasicAuthProviderChecks(t *testing.T, ma mockAuthProvider, cCount, u
t.Errorf("unexpected number of calls to Auth Provider Delete; expctex '%v', got '%v'", dCount, len(ma.d))
}
}
func idname(uid string, resource string) string {
return resource + "-" + uid
}
func idnamea(uid string, resource string) *string {
name := resource + "-" + uid
return &name
}
func addFetchIdExpectation(mock sqlmock.Sqlmock, resource string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
return uid
}
func addFetchExpectation(mock sqlmock.Sqlmock, resource string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "` + resource + `"."id".* FROM "authsrv_` + resource + `" AS "` + resource + `"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, "role-name"))
return uid
}
func addFetchByNameExpectation(mock sqlmock.Sqlmock, resource string, uid string) {
mock.ExpectQuery(`SELECT "` + resource + `"."id", "` + resource + `"."name", .* FROM "authsrv_` + resource + `" AS "` + resource + `" WHERE .*name = '` + resource + `-` + uid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(uid, resource+"-"+uid))
}
func addDeleteExpectation(mock sqlmock.Sqlmock, resource string, uid string) {
mock.ExpectExec(`UPDATE "authsrv_` + resource + `" AS "` + resource + `" SET trash = TRUE WHERE .id = '` + uid).
WillReturnResult(sqlmock.NewResult(1, 1))
}
func addUnavailableExpectation(mock sqlmock.Sqlmock, resource, partner, org, uid string) {
mock.ExpectQuery(`SELECT "` + resource + `"."id" FROM "authsrv_` + resource + `" AS "` + resource + `" WHERE .organization_id = '` + org + `'. AND .partner_id = '` + partner + `'. AND .name = '` + resource + `-` + uid + `'.`).
WillReturnError(fmt.Errorf("no data available"))
}
func addParterOrgFetchExpectation(mock sqlmock.Sqlmock) (string, string) {
pid := uuid.New().String()
oid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(oid))
return pid, oid
}
func addOrgParterFetchExpectation(mock sqlmock.Sqlmock) (string, string) {
pid := uuid.New().String()
oid := uuid.New().String()
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(oid))
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pid))
return pid, oid
}
func addResourceRoleFetchExpectation(mock sqlmock.Sqlmock, scope string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(uid, "role-"+uid, scope))
return uid
}
func addGroupUserMappingsUpdateExpectation(mock sqlmock.Sqlmock, group string) {
mock.ExpectQuery(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE ."group_id" = '` + group + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
}
func addUserGroupMappingsUpdateExpectation(mock sqlmock.Sqlmock, account string) {
mock.ExpectQuery(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE ."account_id" = '` + account + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
}
func addUserIdFetchExpectation(mock sqlmock.Sqlmock) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "identities"."id" FROM "identities" WHERE .*traits ->> 'email' = 'user-` + uid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uid, []byte(`{"email":"user-`+uid+`", "first_name": "John", "last_name": "Doe", "description": "The OG user."}`)))
return uid
}
func addUserFetchExpectation(mock sqlmock.Sqlmock) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "identities"."id".* FROM "identities" WHERE .*traits ->> 'email' = 'user-` + uid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uid, []byte(`{"email":"user-`+uid+`", "first_name": "John", "last_name": "Doe", "description": "The OG user."}`)))
return uid
}
func addUsersGroupFetchExpectation(mock sqlmock.Sqlmock, user string) string {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + user + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + uid))
return uid
}
func addUserRoleMappingsFetchExpectation(mock sqlmock.Sqlmock, user string, project string) {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + user + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + uid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + user + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+uid, "project-"+project))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + user + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+uid, "project-"+project, 7))
}
func addUserRoleMappingsUpdateExpectation(mock sqlmock.Sqlmock, uuuid string) string {
uid := uuid.New().String()
mock.ExpectQuery(`UPDATE "authsrv_accountresourcerole" AS "accountresourcerole" SET trash = TRUE WHERE ."account_id" = '` + uuuid + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
mock.ExpectQuery(`UPDATE "authsrv_projectaccountresourcerole" AS "projectaccountresourcerole" SET trash = TRUE WHERE ."account_id" = '` + uuuid + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
mock.ExpectQuery(`UPDATE "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" SET trash = TRUE WHERE ."account_id" = '` + uuuid + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
return uid
}
func addGroupRoleMappingsFetchExpectation(mock sqlmock.Sqlmock, group string, project string) {
uid := uuid.New().String()
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+uid, "group-"+group))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+uid, "project-"+project))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+uid, "project-"+project, 7))
}
func addGroupRoleMappingsUpdateExpectation(mock sqlmock.Sqlmock, group string) string {
uid := uuid.New().String()
mock.ExpectQuery(`UPDATE "authsrv_grouprole" AS "grouprole" SET trash = TRUE WHERE ."group_id" = '` + group + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
mock.ExpectQuery(`UPDATE "authsrv_projectgrouprole" AS "projectgrouprole" SET trash = TRUE WHERE ."group_id" = '` + group + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
mock.ExpectQuery(`UPDATE "authsrv_projectgroupnamespacerole" AS "projectgroupnamespacerole" SET trash = TRUE WHERE ."group_id" = '` + group + `'. AND .trash = false. RETURNING *`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uid))
return uid
}
func addSentryLookupExpectation(mock sqlmock.Sqlmock, users []string, partner, org string) {
rows := sqlmock.NewRows([]string{"account_id"})
for _, u := range users {
rows.AddRow(u)
}
mock.ExpectQuery(`SELECT DISTINCT account_id FROM "sentry_account_permission" AS "sap" WHERE .partner_id = '` + partner + `'. AND .organization_id = '` + org + `'`).
WithArgs().WillReturnRows(rows)
}
+83 -55
View File
@@ -9,6 +9,7 @@ import (
"github.com/google/uuid"
bun "github.com/uptrace/bun"
"go.uber.org/zap"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/RafayLabs/rcloud-base/internal/dao"
@@ -54,6 +55,7 @@ type userService struct {
azc AuthzService
ks ApiKeyService
cc common.CliConfigDownloadData
al *zap.Logger
}
type userTraits struct {
@@ -70,8 +72,8 @@ type parsedIds struct {
Organization uuid.UUID
}
func NewUserService(ap providers.AuthProvider, db *bun.DB, azc AuthzService, kss ApiKeyService, cfg common.CliConfigDownloadData) UserService {
return &userService{ap: ap, db: db, azc: azc, ks: kss, cc: cfg}
func NewUserService(ap providers.AuthProvider, db *bun.DB, azc AuthzService, kss ApiKeyService, cfg common.CliConfigDownloadData, al *zap.Logger) UserService {
return &userService{ap: ap, db: db, azc: azc, ks: kss, cc: cfg, al: al}
}
func getUserTraits(traits map[string]interface{}) userTraits {
@@ -102,18 +104,18 @@ func getUserTraits(traits map[string]interface{}) userTraits {
}
// Map roles to accounts
func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, user *userv3.User, ids parsedIds) (*userv3.User, error) {
func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, user *userv3.User, ids parsedIds) (*userv3.User, []uuid.UUID, error) {
projectNamespaceRoles := user.GetSpec().GetProjectNamespaceRoles()
// TODO: add transactions
var pars []models.ProjectAccountResourcerole
var ars []models.AccountResourcerole
var ps []*authzv1.Policy
var rids []uuid.UUID
for _, pnr := range projectNamespaceRoles {
role := pnr.GetRole()
entity, err := dao.GetByName(ctx, db, role, &models.Role{})
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
var roleId uuid.UUID
var roleName string
@@ -121,9 +123,10 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
if rle, ok := entity.(*models.Role); ok {
roleId = rle.ID
roleName = rle.Name
rids = append(rids, rle.ID)
scope = strings.ToLower(rle.Scope)
} else {
return &userv3.User{}, fmt.Errorf("unable to find role '%v'", role)
return &userv3.User{}, nil, fmt.Errorf("unable to find role '%v'", role)
}
project := pnr.GetProject()
@@ -153,7 +156,7 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
case "organization":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
ar := models.AccountResourcerole{
@@ -178,14 +181,14 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
case "project":
if org == "" {
return &userv3.User{}, fmt.Errorf("no org name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no org name provided for role '%v'", roleName)
}
if project == "" {
return &userv3.User{}, fmt.Errorf("no project name provided for role '%v'", roleName)
return &userv3.User{}, nil, fmt.Errorf("no project name provided for role '%v'", roleName)
}
projectId, err := dao.GetProjectId(ctx, db, project)
if err != nil {
return user, fmt.Errorf("unable to find project '%v'", project)
return user, nil, fmt.Errorf("unable to find project '%v'", project)
}
par := models.ProjectAccountResourcerole{
CreatedAt: time.Now(),
@@ -210,43 +213,43 @@ func (s *userService) createUserRoleRelations(ctx context.Context, db bun.IDB, u
})
default:
if err != nil {
return user, fmt.Errorf("namespace specific roles are not handled")
return user, nil, fmt.Errorf("namespace specific roles are not handled")
}
}
}
if len(pars) > 0 {
_, err := dao.Create(ctx, db, &pars)
if err != nil {
return &userv3.User{}, err
return &userv3.User{}, nil, err
}
}
if len(ars) > 0 {
_, err := dao.Create(ctx, db, &ars)
if err != nil {
return &userv3.User{}, err
return &userv3.User{}, nil, err
}
}
if len(ps) > 0 {
success, err := s.azc.CreatePolicies(ctx, &authzv1.Policies{Policies: ps})
if err != nil || !success.Res {
return &userv3.User{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.User{}, nil, fmt.Errorf("unable to create mapping in authz; %v", err)
}
}
return user, nil
return user, rids, nil
}
// Update the groups mapped to each user(account)
func (s *userService) createGroupAccountRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, usr *userv3.User) (*userv3.User, error) {
// TODO: add transactions
func (s *userService) createGroupAccountRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, usr *userv3.User) (*userv3.User, []uuid.UUID, error) {
var grpaccs []models.GroupAccount
var ugs []*authzv1.UserGroup
var ids []uuid.UUID
for _, group := range unique(usr.GetSpec().GetGroups()) {
// FIXME: do combined lookup
entity, err := dao.GetByName(ctx, s.db, group, &models.Group{})
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to find group '%v'", group)
return &userv3.User{}, nil, fmt.Errorf("unable to find group '%v'", group)
}
if grp, ok := entity.(*models.Group); ok {
grp := models.GroupAccount{
@@ -257,6 +260,7 @@ func (s *userService) createGroupAccountRelations(ctx context.Context, db bun.ID
GroupId: grp.ID,
Active: true,
}
ids = append(ids, grp.ID)
grpaccs = append(grpaccs, grp)
ugs = append(ugs, &authzv1.UserGroup{
Grp: "g:" + group,
@@ -265,34 +269,38 @@ func (s *userService) createGroupAccountRelations(ctx context.Context, db bun.ID
}
}
if len(grpaccs) == 0 {
return usr, nil
return usr, []uuid.UUID{}, nil
}
_, err := dao.Create(ctx, db, &grpaccs)
if err != nil {
return &userv3.User{}, err
return &userv3.User{}, []uuid.UUID{}, err
}
// TODO: revert our db inserts if this fails
// Just FYI, the success can be false if we delete the db directly but casbin has it available internally
_, err = s.azc.CreateUserGroups(ctx, &authzv1.UserGroups{UserGroups: ugs})
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to create mapping in authz; %v", err)
return &userv3.User{}, []uuid.UUID{}, fmt.Errorf("unable to create mapping in authz; %v", err)
}
return usr, nil
return usr, ids, nil
}
func (s *userService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, usr *userv3.User) (*userv3.User, error) {
err := dao.DeleteX(ctx, db, "account_id", userId, &models.GroupAccount{})
func (s *userService) deleteGroupAccountRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, usr *userv3.User) (*userv3.User, []uuid.UUID, error) {
ugs := []models.GroupAccount{}
ids := []uuid.UUID{}
err := dao.DeleteXR(ctx, db, "account_id", userId, &ugs)
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to delete user; %v", err)
return &userv3.User{}, ids, fmt.Errorf("unable to delete user; %v", err)
}
_, err = s.azc.DeleteUserGroups(ctx, &authzv1.UserGroup{Grp: "u:" + usr.GetMetadata().GetName()})
if err != nil {
return &userv3.User{}, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
return &userv3.User{}, ids, fmt.Errorf("unable to delete group-user relations from authz; %v", err)
}
return usr, nil
for _, ug := range ugs {
ids = append(ids, ug.GroupId)
}
return usr, ids, nil
}
// FIXME: make this generic
@@ -312,7 +320,6 @@ func (s *userService) getPartnerOrganization(ctx context.Context, db bun.IDB, us
}
func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.User, error) {
// TODO: restrict endpoint to admin
partnerId, organizationId, err := s.getPartnerOrganization(ctx, s.db, user)
if err != nil {
return nil, fmt.Errorf("unable to get partner and org id")
@@ -336,13 +343,13 @@ func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.Us
return &userv3.User{}, err
}
user, err = s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: uid, Partner: partnerId, Organization: organizationId})
user, rolesAfter, err := s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: uid, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.User{}, err
}
user, err = s.createGroupAccountRelations(ctx, tx, uuid.MustParse(id), user)
user, groupsAfter, err := s.createGroupAccountRelations(ctx, tx, uuid.MustParse(id), user)
if err != nil {
tx.Rollback()
return &userv3.User{}, err
@@ -361,6 +368,7 @@ func (s *userService) Create(ctx context.Context, user *userv3.User) (*userv3.Us
}
user.Spec.RecoveryUrl = &rl
CreateUserAuditEvent(ctx, s.al, s.db, AuditActionCreate, user.GetMetadata().GetName(), uid, []uuid.UUID{}, rolesAfter, []uuid.UUID{}, groupsAfter)
return user, nil
}
@@ -452,10 +460,10 @@ func (s *userService) GetByName(ctx context.Context, user *userv3.User) (*userv3
}
func (s *userService) GetUserInfo(ctx context.Context, user *userv3.User) (*userv3.UserInfo, error) {
sd, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sd, ok := GetSessionDataFromContext(ctx)
username := ""
if !ok {
return &userv3.UserInfo{}, fmt.Errorf("cannot perform project listing without auth")
return &userv3.UserInfo{}, fmt.Errorf("cannot get user info without auth")
}
username = sd.Username
@@ -517,26 +525,42 @@ func (s *userService) GetUserInfo(ctx context.Context, user *userv3.User) (*user
return &userv3.UserInfo{}, fmt.Errorf("unable to get user info")
}
func (s *userService) deleteUserRoleRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, user *userv3.User) error {
err := dao.DeleteX(ctx, db, "account_id", userId, &models.AccountResourcerole{})
func (s *userService) deleteUserRoleRelations(ctx context.Context, db bun.IDB, userId uuid.UUID, user *userv3.User) ([]uuid.UUID, error) {
ids := []uuid.UUID{}
ar := []models.AccountResourcerole{}
err := dao.DeleteXR(ctx, db, "account_id", userId, &ar)
if err != nil {
return err
return nil, err
}
err = dao.DeleteX(ctx, db, "account_id", userId, &models.ProjectAccountResourcerole{})
if err != nil {
return err
for _, r := range ar {
ids = append(ids, r.RoleId)
}
err = dao.DeleteX(ctx, db, "account_id", userId, &models.ProjectAccountNamespaceRole{})
par := []models.ProjectAccountResourcerole{}
err = dao.DeleteXR(ctx, db, "account_id", userId, &par)
if err != nil {
return err
return nil, err
}
for _, r := range par {
ids = append(ids, r.RoleId)
}
panr := []models.ProjectAccountNamespaceRole{}
err = dao.DeleteXR(ctx, db, "account_id", userId, &panr)
if err != nil {
return nil, err
}
for _, r := range panr {
ids = append(ids, r.RoleId)
}
_, err = s.azc.DeletePolicies(ctx, &authzv1.Policy{Sub: "u:" + user.GetMetadata().GetName()})
if err != nil {
return fmt.Errorf("unable to delete user-role relations from authz; %v", err)
return nil, fmt.Errorf("unable to delete user-role relations from authz; %v", err)
}
return nil
return ids, nil
}
func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.User, error) {
@@ -566,25 +590,25 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
return &userv3.User{}, err
}
err = s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
rolesBefore, err := s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userv3.User{}, err
}
user, err = s.deleteGroupAccountRelations(ctx, tx, usr.ID, user)
user, groupsBefore, err := s.deleteGroupAccountRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userv3.User{}, err
}
user, err = s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: usr.ID, Partner: partnerId, Organization: organizationId})
user, rolesAfter, err := s.createUserRoleRelations(ctx, tx, user, parsedIds{Id: usr.ID, Partner: partnerId, Organization: organizationId})
if err != nil {
tx.Rollback()
return &userv3.User{}, err
}
user, err = s.createGroupAccountRelations(ctx, tx, usr.ID, user)
user, groupsAfter, err := s.createGroupAccountRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userv3.User{}, err
@@ -595,6 +619,8 @@ func (s *userService) Update(ctx context.Context, user *userv3.User) (*userv3.Us
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateUserAuditEvent(ctx, s.al, s.db, AuditActionUpdate, user.GetMetadata().GetName(), usr.ID, rolesBefore, rolesAfter, groupsBefore, groupsAfter)
return user, nil
} else {
@@ -617,29 +643,31 @@ func (s *userService) Delete(ctx context.Context, user *userv3.User) (*userrpcv3
return &userrpcv3.DeleteUserResponse{}, err
}
err = s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
rolesBefore, err := s.deleteUserRoleRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, err
}
user, groupsBefore, err := s.deleteGroupAccountRelations(ctx, tx, usr.ID, user)
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, fmt.Errorf("unable to delete user; %v", err)
}
err = s.ap.Delete(ctx, usr.ID.String())
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, err
}
err = dao.DeleteX(ctx, tx, "account_id", usr.ID, &models.GroupAccount{})
if err != nil {
tx.Rollback()
return &userrpcv3.DeleteUserResponse{}, fmt.Errorf("unable to delete user; %v", err)
}
err = tx.Commit()
if err != nil {
tx.Rollback()
_log.Warn("unable to commit changes", err)
}
CreateUserAuditEvent(ctx, s.al, s.db, AuditActionDelete, user.GetMetadata().GetName(), usr.ID, rolesBefore, []uuid.UUID{}, groupsBefore, []uuid.UUID{})
return &userrpcv3.DeleteUserResponse{}, nil
}
return &userrpcv3.DeleteUserResponse{}, fmt.Errorf("unable to delete user '%v'", user.Metadata.Name)
+172 -363
View File
@@ -62,17 +62,10 @@ func TestCreateUser(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
mock.ExpectCommit()
@@ -92,25 +85,22 @@ func TestCreateUser(t *testing.T) {
}
func TestCreateUserWithRole(t *testing.T) {
pruuid := uuid.New().String()
prname := "project-" + pruuid
ruuid := uuid.New().String()
rname := "project-" + ruuid
var namespaceid int64 = 7
tt := []struct {
name string
roles []*userv3.ProjectNamespaceRole
role bool
project bool
namespace bool
dbname string
scope string
shouldfail bool
}{
{"just role", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "system", false},
{"just role org scope", []*userv3.ProjectNamespaceRole{{Role: rname}}, "authsrv_accountresourcerole", "organization", false},
{"just project", []*userv3.ProjectNamespaceRole{{Project: &prname}}, "authsrv_accountrole", "system", true}, // no role creation without role
{"just namespace", []*userv3.ProjectNamespaceRole{{Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid}}, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and role", []*userv3.ProjectNamespaceRole{{Project: &prname, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
{"project role namespace", []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}, "authsrv_projectaccountresourcerole", "project", false},
{"just role", true, false, false, "authsrv_accountresourcerole", "system", false},
{"just role org scope", true, false, false, "authsrv_accountresourcerole", "organization", false},
{"just project", false, true, false, "authsrv_accountrole", "system", true}, // no role creation without role
{"just namespace", false, false, true, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and namespace", false, true, true, "authsrv_accountrole", "system", true}, // no role creation without role,
{"project and role", true, true, false, "authsrv_projectaccountresourcerole", "project", false},
{"project role namespace", true, true, true, "authsrv_projectaccountresourcerole", "project", false},
}
for _, tc := range tt {
@@ -120,24 +110,25 @@ func TestCreateUserWithRole(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
puuid := uuid.New().String()
pruuid := uuid.New().String()
ouuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", tc.scope))
if tc.roles[0].Project != nil {
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
ruuid := addResourceRoleFetchExpectation(mock, tc.scope)
role := &userv3.ProjectNamespaceRole{}
if tc.role {
role.Role = idname(ruuid, "role")
}
if tc.project {
pruuid := addFetchIdExpectation(mock, "project")
role.Project = &pruuid
}
if tc.namespace {
var ns int64 = 7
role.Namespace = &ns
}
mock.ExpectQuery(fmt.Sprintf(`INSERT INTO "%v"`, tc.dbname)).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
@@ -145,8 +136,9 @@ func TestCreateUserWithRole(t *testing.T) {
user := &userv3.User{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "user-" + uuuid},
Spec: &userv3.UserSpec{ProjectNamespaceRoles: tc.roles},
Spec: &userv3.UserSpec{ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{role}},
}
user, err := us.Create(context.Background(), user)
if tc.shouldfail {
if err == nil {
@@ -175,46 +167,24 @@ func TestUpdateUser(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
uuuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
pruuid := uuid.New().String()
prname := "project-" + pruuid
ruuid := uuid.New().String()
rname := "project-" + ruuid
var namespaceid int64 = 7
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
// performing update
mock.ExpectQuery(`SELECT "identities"."id" FROM "identities" WHERE .*traits ->> 'email' = 'user-` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
uuuid := addUserIdFetchExpectation(mock)
puuid, ouuid := addParterOrgFetchExpectation(mock)
mock.ExpectBegin()
mock.ExpectExec(`UPDATE "authsrv_accountresourcerole" AS "accountresourcerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectaccountresourcerole" AS "projectaccountresourcerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectQuery(`SELECT "resourcerole"."id".* FROM "authsrv_resourcerole" AS "resourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "name", "scope"}).AddRow(pruuid, "role-name", "project"))
mock.ExpectQuery(`SELECT "project"."id" FROM "authsrv_project" AS "project"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(pruuid))
_ = addUserRoleMappingsUpdateExpectation(mock, uuuid)
addUserGroupMappingsUpdateExpectation(mock, uuuid)
ruuid := addResourceRoleFetchExpectation(mock, "project")
pruuid := addFetchExpectation(mock, "project")
mock.ExpectQuery(`INSERT INTO "authsrv_projectaccountresourcerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(uuid.New().String()))
mock.ExpectCommit()
var ns int64 = 7
user := &userv3.User{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Name: "user-" + uuuid},
Spec: &userv3.UserSpec{ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{{Project: &prname, Namespace: &namespaceid, Role: rname}}},
Spec: &userv3.UserSpec{ProjectNamespaceRoles: []*userv3.ProjectNamespaceRole{{Project: idnamea(pruuid, "project"), Namespace: &ns, Role: idname(ruuid, "role")}}},
}
user, err := us.Update(context.Background(), user)
if err != nil {
@@ -233,28 +203,29 @@ func TestUserGetByName(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
guuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "identities"."id", .* FROM "identities" WHERE .*traits ->> 'email' = 'user-` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
uuuid := addUserFetchExpectation(mock)
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid + `'`).
@@ -268,8 +239,8 @@ func TestUserGetByName(t *testing.T) {
t.Fatal("could not get user:", err)
}
performUserBasicChecks(t, user, uuuid)
if user.GetMetadata().GetName() != "johndoe@provider.com" {
t.Errorf("invalid email for user, expected johndoe@provider.com; got '%v'", user.GetMetadata().GetName())
if user.GetMetadata().GetName() != "user-"+uuuid {
t.Errorf("invalid email for user, expected '%v'; got '%v'", "user-"+uuuid, user.GetMetadata().GetName())
}
if len(user.GetSpec().GetGroups()) != 1 {
t.Errorf("invalid number of groups returned for user, expected 2; got '%v'", len(user.GetSpec().GetGroups()))
@@ -289,7 +260,7 @@ func TestUserGetInfo(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
fakeuuuid := uuid.New().String()
@@ -361,32 +332,20 @@ func TestUserGetById(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
guuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
// lookup by id
mock.ExpectQuery(`SELECT "identities"."id",.* FROM "identities" WHERE .*id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
guuid := addUsersGroupFetchExpectation(mock, uuuid)
addGroupRoleMappingsFetchExpectation(mock, guuid, pruuid)
addUserRoleMappingsFetchExpectation(mock, uuuid, pruuid)
user := &userv3.User{
Metadata: &v3.Metadata{Partner: "partner-" + puuid, Organization: "org-" + ouuid, Id: uuuid},
@@ -402,276 +361,132 @@ func TestUserGetById(t *testing.T) {
if len(user.GetSpec().GetProjectNamespaceRoles()) != 6 {
t.Errorf("invalid number of roles returned for user, expected 6; got '%v'", len(user.GetSpec().GetProjectNamespaceRoles()))
}
if user.GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 9 {
t.Errorf("invalid namespace in role returned for user, expected 9; got '%v'", user.GetSpec().GetProjectNamespaceRoles()[2].Namespace)
if user.GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 7 {
t.Errorf("invalid namespace in role returned for user, expected 7; got '%v'", user.GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
performBasicAuthProviderChecks(t, *ap, 0, 0, 0, 0)
}
func TestUserList(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
uuuid1 := uuid.New().String()
uuuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
guuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "identities"."id", .*FROM "identities" LIMIT 10`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
qo := &commonv3.QueryOptions{Organization: ouuid, Partner: puuid}
userlist, err := us.List(context.Background(), query.WithOptions(qo))
if err != nil {
t.Fatal("could not list users:", err)
tests := []struct {
name string
q string
limit int64
offset int64
orderBy string
order string
role string
group string
projects []string
utype string
}{
{"simple list", "", 50, 20, "", "", "", "", []string{}, ""},
{"simple list with type", "", 50, 20, "", "", "", "", []string{}, "password"},
{"sorted list", "", 50, 20, "email", "asc", "", "", []string{}, ""},
{"sorted list without dir", "", 50, 20, "email", "", "", "", []string{}, ""},
{"sorted list with q", "filter-query", 50, 20, "email", "asc", "", "", []string{}, ""},
{"sorted list with role", "", 50, 20, "email", "asc", "role-name", "", []string{}, ""},
{"sorted list with role and group", "", 50, 20, "email", "asc", "role-name", "group-name", []string{}, ""},
{"sorted list with q and role", "filter-query", 50, 20, "email", "asc", "role-name", "", []string{}, ""},
}
if userlist.Metadata.Count != 2 {
t.Fatalf("incorrect number of users returned, expected 2; got %v", userlist.Metadata.Count)
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid1 := uuid.New().String()
uuuid2 := uuid.New().String()
pruuid := uuid.New().String()
puuid, ouuid := addParterOrgFetchExpectation(mock)
q := ""
if tc.q != "" {
q = ` AND .traits ->> 'email' ILIKE '%` + tc.q + `%'. OR .traits ->> 'first_name' ILIKE '%` + tc.q + `%'. OR .traits ->> 'last_name' ILIKE '%` + tc.q + `%'. `
}
order := ""
if tc.orderBy != "" {
order = `ORDER BY "traits ->> '` + tc.orderBy + `' `
}
if tc.order != "" {
order = order + tc.order + `" `
}
if tc.role != "" {
addFetchExpectation(mock, "resourcerole")
}
if tc.group != "" {
addFetchExpectation(mock, "group")
}
if tc.role != "" || tc.group != "" || len(tc.projects) != 0 {
addSentryLookupExpectation(mock, []string{uuuid1, uuuid2}, puuid, ouuid)
mock.ExpectQuery(`SELECT "identities"."id", .*WHERE .id IN .'` + uuuid1 + `', '` + uuuid2 + `'.. ` + q + order + `LIMIT ` + fmt.Sprint(tc.limit) + ` OFFSET ` + fmt.Sprint(tc.offset)).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
} else {
if tc.utype != "" {
mock.ExpectQuery(`SELECT "identities"."id", .*, "identity_credential"."id" AS "identity_credential__id", .*FROM "identities" LEFT JOIN "identity_credentials" AS "identity_credential" ON ."identity_credential"."identity_id" = "identities"."id". LEFT JOIN "identity_credential_types" AS "identity_credential__identity_credential_type" ON ."identity_credential__identity_credential_type"."id" = "identity_credential"."identity_credential_type_id". WHERE .name = '` + tc.utype + `'. LIMIT 50 OFFSET 20`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
} else {
mock.ExpectQuery(`SELECT "identities"."id".* LIMIT 50 OFFSET 20$`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
}
}
guuid := addUsersGroupFetchExpectation(mock, uuuid1)
addGroupRoleMappingsFetchExpectation(mock, guuid, pruuid)
addUserRoleMappingsFetchExpectation(mock, uuuid1, pruuid)
guuid = addUsersGroupFetchExpectation(mock, uuuid2)
addGroupRoleMappingsFetchExpectation(mock, guuid, pruuid)
addUserRoleMappingsFetchExpectation(mock, uuuid2, pruuid)
qo := &commonv3.QueryOptions{
Q: tc.q,
Limit: tc.limit,
Offset: tc.offset,
OrderBy: tc.orderBy,
Order: tc.order,
Organization: ouuid,
Partner: puuid,
Role: tc.role,
Group: tc.group,
Type: tc.utype,
}
userlist, err := us.List(context.Background(), query.WithOptions(qo))
if err != nil {
t.Fatal("could not list users:", err)
}
if userlist.Metadata.Count != 2 {
t.Fatalf("incorrect number of users returned, expected 2; got %v", userlist.Metadata.Count)
}
if userlist.Items[0].Metadata.Name != "johndoe@provider.com" || userlist.Items[1].Metadata.Name != "johndoe@provider.com" {
t.Errorf("incorrect user names returned when listing; expected '%v' and '%v'; got '%v' and '%v'", "johndoe@provider.com", "johndoe@provider.com", userlist.Items[0].Metadata.Name, userlist.Items[1].Metadata.Name)
}
if len(userlist.Items[0].GetSpec().GetGroups()) != 1 {
t.Errorf("invalid number of groups returned for user, expected 1; got '%v'", len(userlist.Items[0].GetSpec().GetGroups()))
}
if len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()) != 6 {
t.Errorf("invalid number of roles returned for user, expected 6; got '%v'", len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()))
}
if userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 7 {
t.Errorf("invalid namespace in role returned for user, expected 7; got '%v'", userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
performBasicAuthProviderChecks(t, *ap, 0, 0, 0, 0)
})
}
if userlist.Items[0].Metadata.Name != "johndoe@provider.com" || userlist.Items[1].Metadata.Name != "johndoe@provider.com" {
t.Errorf("incorrect user names returned when listing; expected '%v' and '%v'; got '%v' and '%v'", "johndoe@provider.com", "johndoe@provider.com", userlist.Items[0].Metadata.Name, userlist.Items[1].Metadata.Name)
}
if len(userlist.Items[0].GetSpec().GetGroups()) != 1 {
t.Errorf("invalid number of groups returned for user, expected 1; got '%v'", len(userlist.Items[0].GetSpec().GetGroups()))
}
if len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()) != 6 {
t.Errorf("invalid number of roles returned for user, expected 6; got '%v'", len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()))
}
if userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 9 {
t.Errorf("invalid namespace in role returned for user, expected 9; got '%v'", userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
performBasicAuthProviderChecks(t, *ap, 0, 0, 0, 0)
}
func TestUserListWithType(t *testing.T) {
// TODO: merge these tests
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
uuuid1 := uuid.New().String()
uuuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
guuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "identities"."id", "identities"."schema_id", "identities"."traits", "identities"."created_at", "identities"."updated_at", "identities"."state", "identities"."state_changed_at", "identities"."nid", "identity_credential"."id" AS "identity_credential__id", "identity_credential"."identity_id" AS "identity_credential__identity_id", "identity_credential"."identity_credential_type_id" AS "identity_credential__identity_credential_type_id", "identity_credential__identity_credential_type"."id" AS "identity_credential__identity_credential_type__id", "identity_credential__identity_credential_type"."name" AS "identity_credential__identity_credential_type__name" FROM "identities" LEFT JOIN "identity_credentials" AS "identity_credential" ON ."identity_credential"."identity_id" = "identities"."id". LEFT JOIN "identity_credential_types" AS "identity_credential__identity_credential_type" ON ."identity_credential__identity_credential_type"."id" = "identity_credential"."identity_credential_type_id". WHERE .name = 'password'. LIMIT 10`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
qo := &commonv3.QueryOptions{Organization: ouuid, Partner: puuid, Type: "password"}
userlist, err := us.List(context.Background(), query.WithOptions(qo))
if err != nil {
t.Fatal("could not list users:", err)
}
if userlist.Metadata.Count != 2 {
t.Fatalf("incorrect number of users returned, expected 2; got %v", userlist.Metadata.Count)
}
if userlist.Items[0].Metadata.Name != "johndoe@provider.com" || userlist.Items[1].Metadata.Name != "johndoe@provider.com" {
t.Errorf("incorrect user names returned when listing; expected '%v' and '%v'; got '%v' and '%v'", "johndoe@provider.com", "johndoe@provider.com", userlist.Items[0].Metadata.Name, userlist.Items[1].Metadata.Name)
}
if len(userlist.Items[0].GetSpec().GetGroups()) != 1 {
t.Errorf("invalid number of groups returned for user, expected 1; got '%v'", len(userlist.Items[0].GetSpec().GetGroups()))
}
if len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()) != 6 {
t.Errorf("invalid number of roles returned for user, expected 6; got '%v'", len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()))
}
if userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 9 {
t.Errorf("invalid namespace in role returned for user, expected 9; got '%v'", userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
performBasicAuthProviderChecks(t, *ap, 0, 0, 0, 0)
}
func TestUserFiltered(t *testing.T) {
db, mock := getDB(t)
defer db.Close()
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
uuuid1 := uuid.New().String()
uuuid2 := uuid.New().String()
puuid := uuid.New().String()
ouuid := uuid.New().String()
guuid := uuid.New().String()
ruuid := uuid.New().String()
pruuid := uuid.New().String()
mock.ExpectQuery(`SELECT "partner"."id" FROM "authsrv_partner" AS "partner"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(puuid))
mock.ExpectQuery(`SELECT "organization"."id" FROM "authsrv_organization" AS "organization"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(ouuid))
mock.ExpectQuery(`SELECT "group"."id" FROM "authsrv_group" AS "group" WHERE .name = 'group-name'. AND .trash = FALSE.`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id"}).AddRow(guuid))
mock.ExpectQuery(`SELECT DISTINCT account_id FROM "sentry_account_permission" AS "sap" WHERE .partner_id = '` + puuid + `'. AND .organization_id = '` + ouuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"account_id"}).AddRow(uuuid1).AddRow(uuuid2))
mock.ExpectQuery(`SELECT "identities"."id", .*WHERE .id IN .'` + uuuid1 + `', '` + uuuid2 + `'.. AND .traits ->> 'email' ILIKE '%filter-query%'. OR .traits ->> 'first_name' ILIKE '%filter-query%'. OR .traits ->> 'last_name' ILIKE '%filter-query%'. ORDER BY "traits ->> 'email' asc" LIMIT 50 OFFSET 20`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).
AddRow(uuuid1, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)).
AddRow(uuuid2, []byte(`{"email":"johndoe@provider.com", "first_name": "John", "last_name": "Doe", "organization_id": "`+ouuid+`", "partner_id": "`+puuid+`", "description": "My awesome user"}`)))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid).AddRow("group2-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group2-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid1 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
mock.ExpectQuery(`SELECT "group"."id".* FROM "authsrv_group" AS "group" JOIN authsrv_groupaccount ON authsrv_groupaccount.group_id="group".id WHERE .authsrv_groupaccount.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"name"}).
AddRow("group-" + guuid).AddRow("group2-" + guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_group.name as group FROM "authsrv_grouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_grouprole.role_id JOIN authsrv_group ON authsrv_group.id=authsrv_grouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "group"}).AddRow("role-"+ruuid, "group2-"+guuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, authsrv_group.name as group FROM "authsrv_projectgrouprole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectgrouprole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectgrouprole.project_id JOIN authsrv_group ON authsrv_group.id=authsrv_projectgrouprole.group_id WHERE`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace, authsrv_group.name as group FROM "authsrv_projectgroupnamespacerole"`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+puuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role FROM "authsrv_accountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_accountresourcerole.role_id WHERE .authsrv_accountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role"}).AddRow("role-" + ruuid))
mock.ExpectQuery(`SELECT distinct authsrv_resourcerole.name as role, authsrv_project.name as project FROM "authsrv_projectaccountresourcerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountresourcerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountresourcerole.project_id WHERE .authsrv_projectaccountresourcerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project"}).AddRow("role-"+ruuid, "project-"+pruuid))
mock.ExpectQuery(`SELECT authsrv_resourcerole.name as role, authsrv_project.name as project, namespace_id as namespace FROM "authsrv_projectaccountnamespacerole" JOIN authsrv_resourcerole ON authsrv_resourcerole.id=authsrv_projectaccountnamespacerole.role_id JOIN authsrv_project ON authsrv_project.id=authsrv_projectaccountnamespacerole.project_id WHERE .authsrv_projectaccountnamespacerole.account_id = '` + uuuid2 + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"role", "project", "namespace"}).AddRow("role-"+ruuid, "project-"+pruuid, 9))
qo := &commonv3.QueryOptions{Q: "filter-query", Limit: 50, Offset: 20, OrderBy: "email", Order: "asc", Organization: ouuid, Partner: puuid, Group: "group-name"}
userlist, err := us.List(context.Background(), query.WithOptions(qo))
if err != nil {
t.Fatal("could not list users:", err)
}
if userlist.Metadata.Count != 2 {
t.Fatalf("incorrect number of users returned, expected 2; got %v", userlist.Metadata.Count)
}
if userlist.Items[0].Metadata.Name != "johndoe@provider.com" {
t.Errorf("incorrect user names returned when listing; expected '%v' and '%v'; got '%v' and '%v'", "johndoe@provider.com", "johndoe@provider.com", userlist.Items[0].Metadata.Name, userlist.Items[1].Metadata.Name)
}
if len(userlist.Items[0].GetSpec().GetGroups()) != 2 {
t.Errorf("invalid number of groups returned for user, expected 2; got '%v'", len(userlist.Items[0].GetSpec().GetGroups()))
}
if len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()) != 9 {
t.Errorf("invalid number of roles returned for user, expected 3; got '%v'", len(userlist.Items[0].GetSpec().GetProjectNamespaceRoles()))
}
if userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].GetNamespace() != 9 {
t.Errorf("invalid namespace in role returned for user, expected 9; got '%v'", userlist.Items[0].GetSpec().GetProjectNamespaceRoles()[2].Namespace)
}
performBasicAuthProviderChecks(t, *ap, 0, 0, 0, 0)
}
func TestUserDelete(t *testing.T) {
@@ -680,7 +495,7 @@ func TestUserDelete(t *testing.T) {
ap := &mockAuthProvider{}
mazc := mockAuthzClient{}
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{})
us := NewUserService(ap, db, &mazc, nil, common.CliConfigDownloadData{}, getLogger())
uuuid := uuid.New().String()
puuid := uuid.New().String()
@@ -689,15 +504,9 @@ func TestUserDelete(t *testing.T) {
mock.ExpectQuery(`SELECT "identities"."id" FROM "identities" WHERE .*traits ->> 'email' = 'user-` + uuuid + `'`).
WithArgs().WillReturnRows(sqlmock.NewRows([]string{"id", "traits"}).AddRow(uuuid, []byte(`{"email":"johndoe@provider.com"}`)))
mock.ExpectBegin()
mock.ExpectExec(`UPDATE "authsrv_accountresourcerole" AS "accountresourcerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectaccountresourcerole" AS "projectaccountresourcerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`UPDATE "authsrv_projectaccountnamespacerole" AS "projectaccountnamespacerole" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
_ = addUserRoleMappingsUpdateExpectation(mock, uuuid)
// User delete is via kratos
mock.ExpectExec(`UPDATE "authsrv_groupaccount" AS "groupaccount" SET trash = TRUE WHERE`).
WillReturnResult(sqlmock.NewResult(1, 1))
addUserGroupMappingsUpdateExpectation(mock, uuuid)
mock.ExpectCommit()
user := &userv3.User{
+57
View File
@@ -4,6 +4,8 @@ import (
"context"
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/pkg/common"
commonv3 "github.com/RafayLabs/rcloud-base/proto/types/commonpb/v3"
"github.com/google/uuid"
"github.com/uptrace/bun"
)
@@ -29,6 +31,15 @@ func contains(s []string, str string) bool {
return false
}
func containsu(s []uuid.UUID, id uuid.UUID) bool {
for _, v := range s {
if v == id {
return true
}
}
return false
}
func remove(l []string, item string) []string {
for i, other := range l {
if other == item {
@@ -38,6 +49,47 @@ func remove(l []string, item string) []string {
return l
}
func diff(before, after []string) ([]string, []string, []string) {
cu := []string{}
uu := []string{}
du := []string{}
for _, u := range after {
if contains(before, u) {
uu = append(uu, u)
} else {
cu = append(du, u)
}
}
for _, u := range before {
if !contains(uu, u) && !contains(du, u) {
du = append(cu, u)
}
}
return cu, uu, du
}
// Given two lists, return newly created, unchanged and deleted items
func diffu(before, after []uuid.UUID) ([]uuid.UUID, []uuid.UUID, []uuid.UUID) {
cu := []uuid.UUID{}
uu := []uuid.UUID{}
du := []uuid.UUID{}
for _, u := range after {
if containsu(before, u) {
uu = append(uu, u)
} else {
cu = append(du, u)
}
}
for _, u := range before {
if !containsu(uu, u) && !containsu(du, u) {
du = append(cu, u)
}
}
return cu, uu, du
}
func getPartnerOrganization(ctx context.Context, db bun.IDB, partner, org string) (uuid.UUID, uuid.UUID, error) {
partnerId, err := dao.GetPartnerId(ctx, db, partner)
if err != nil {
@@ -50,3 +102,8 @@ func getPartnerOrganization(ctx context.Context, db bun.IDB, partner, org string
return partnerId, organizationId, nil
}
func GetSessionDataFromContext(ctx context.Context) (*commonv3.SessionData, bool) {
s, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
return s, ok
}
+106 -76
View File
@@ -516,9 +516,12 @@ type SessionData struct {
Idp string `protobuf:"bytes,15,opt,name=idp,proto3" json:"idp,omitempty"`
IsOrgAdmin map[string]bool `protobuf:"bytes,16,rep,name=is_org_admin,json=isOrgAdmin,proto3" json:"is_org_admin,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
ClientType ClientType `protobuf:"varint,17,opt,name=client_type,json=clientType,proto3,enum=rafay.dev.types.common.v3.ClientType" json:"client_type,omitempty"`
IsAllNsAccess map[string]bool `protobuf:"bytes,18,rep,name=is_all_ns_access,json=isAllNsAccess,proto3" json:"is_all_ns_access,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
Namespaces []*NamespaceData `protobuf:"bytes,19,rep,name=namespaces,proto3" json:"namespaces,omitempty"`
Project *ProjectData `protobuf:"bytes,20,opt,name=project,proto3" json:"project,omitempty"`
ClientHost string `protobuf:"bytes,18,opt,name=client_host,json=clientHost,proto3" json:"client_host,omitempty"`
ClientIp string `protobuf:"bytes,19,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
ClientUa string `protobuf:"bytes,20,opt,name=client_ua,json=clientUa,proto3" json:"client_ua,omitempty"`
IsAllNsAccess map[string]bool `protobuf:"bytes,21,rep,name=is_all_ns_access,json=isAllNsAccess,proto3" json:"is_all_ns_access,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"`
Namespaces []*NamespaceData `protobuf:"bytes,22,rep,name=namespaces,proto3" json:"namespaces,omitempty"`
Project *ProjectData `protobuf:"bytes,23,opt,name=project,proto3" json:"project,omitempty"`
}
func (x *SessionData) Reset() {
@@ -672,6 +675,27 @@ func (x *SessionData) GetClientType() ClientType {
return ClientType_ClientTypeNotSet
}
func (x *SessionData) GetClientHost() string {
if x != nil {
return x.ClientHost
}
return ""
}
func (x *SessionData) GetClientIp() string {
if x != nil {
return x.ClientIp
}
return ""
}
func (x *SessionData) GetClientUa() string {
if x != nil {
return x.ClientUa
}
return ""
}
func (x *SessionData) GetIsAllNsAccess() map[string]bool {
if x != nil {
return x.IsAllNsAccess
@@ -796,7 +820,7 @@ var file_proto_types_commonpb_v3_auth_proto_rawDesc = []byte{
0x6c, 0x12, 0x3a, 0x0a, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,
0x26, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65,
0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x72, 0x6f, 0x6a,
0x65, 0x63, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0xae, 0x09,
0x65, 0x63, 0x74, 0x52, 0x6f, 0x6c, 0x65, 0x52, 0x04, 0x6c, 0x69, 0x73, 0x74, 0x22, 0x89, 0x0a,
0x0a, 0x0b, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x12, 0x18, 0x0a,
0x07, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e,
@@ -841,80 +865,86 @@ var file_proto_types_commonpb_v3_auth_proto_rawDesc = []byte{
0x74, 0x79, 0x70, 0x65, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x72, 0x61, 0x66,
0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70,
0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x62, 0x0a,
0x10, 0x69, 0x73, 0x5f, 0x61, 0x6c, 0x6c, 0x5f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73,
0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x39, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e,
0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1f, 0x0a,
0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x6f, 0x73, 0x74, 0x18, 0x12, 0x20, 0x01,
0x28, 0x09, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1b,
0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x13, 0x20, 0x01, 0x28,
0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x63,
0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x75, 0x61, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x55, 0x61, 0x12, 0x62, 0x0a, 0x10, 0x69, 0x73, 0x5f, 0x61,
0x6c, 0x6c, 0x5f, 0x6e, 0x73, 0x5f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x15, 0x20, 0x03,
0x28, 0x0b, 0x32, 0x39, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74,
0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x73, 0x41, 0x6c, 0x6c,
0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x69,
0x73, 0x41, 0x6c, 0x6c, 0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x48, 0x0a, 0x0a,
0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18, 0x16, 0x20, 0x03, 0x28, 0x0b,
0x32, 0x28, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70,
0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x4e, 0x61, 0x6d,
0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0a, 0x6e, 0x61, 0x6d, 0x65,
0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63,
0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e,
0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e,
0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x2e,
0x49, 0x73, 0x41, 0x6c, 0x6c, 0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x52, 0x0d, 0x69, 0x73, 0x41, 0x6c, 0x6c, 0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73,
0x73, 0x12, 0x48, 0x0a, 0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x18,
0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65,
0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76,
0x33, 0x2e, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x44, 0x61, 0x74, 0x61, 0x52,
0x0a, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x07, 0x70,
0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72,
0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74,
0x44, 0x61, 0x74, 0x61, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x1a, 0x6e, 0x0a,
0x11, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6b, 0x65, 0x79, 0x12, 0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x2d, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e,
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x55, 0x52, 0x4c, 0x4d, 0x65, 0x74, 0x68, 0x6f,
0x64, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a,
0x0f, 0x49, 0x73, 0x4f, 0x72, 0x67, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79,
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12,
0x49, 0x73, 0x41, 0x6c, 0x6c, 0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74,
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe,
0x01, 0x0a, 0x18, 0x49, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x6f,
0x77, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x73,
0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x72, 0x61,
0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f,
0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53,
0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a,
0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72,
0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x48, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x44, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x61, 0x66,
0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61,
0x74, 0x61, 0x52, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x2a,
0x6f, 0x0a, 0x0d, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x1b, 0x0a,
0x17, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65,
0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x65, 0x64, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x72, 0x55, 0x52, 0x4c,
0x4e, 0x6f, 0x74, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x10, 0x03,
0x2a, 0x3c, 0x0a, 0x08, 0x41, 0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e,
0x41, 0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x6f, 0x74, 0x53, 0x65, 0x74, 0x10, 0x00,
0x12, 0x10, 0x0a, 0x0c, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x10, 0x02, 0x2a, 0x38,
0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10,
0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x6f, 0x74, 0x53, 0x65, 0x74,
0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x42, 0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x10, 0x01, 0x12,
0x07, 0x0a, 0x03, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x42, 0xf6, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d,
0x2e, 0x76, 0x33, 0x2e, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x44, 0x61, 0x74, 0x61, 0x52,
0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x1a, 0x6e, 0x0a, 0x11, 0x52, 0x65, 0x73, 0x6f,
0x75, 0x72, 0x63, 0x65, 0x55, 0x72, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x43, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2d,
0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68,
0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e,
0x63, 0x6f, 0x6d, 0x2f, 0x52, 0x61, 0x66, 0x61, 0x79, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x72, 0x63,
0x6c, 0x6f, 0x75, 0x64, 0x2d, 0x62, 0x61, 0x73, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x76,
0x33, 0x3b, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x76, 0x33, 0xa2, 0x02, 0x04, 0x52, 0x44, 0x54,
0x43, 0xaa, 0x02, 0x19, 0x52, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x44, 0x65, 0x76, 0x2e, 0x54, 0x79,
0x70, 0x65, 0x73, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x33, 0xca, 0x02, 0x19,
0x52, 0x61, 0x66, 0x61, 0x79, 0x5c, 0x44, 0x65, 0x76, 0x5c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c,
0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5c, 0x56, 0x33, 0xe2, 0x02, 0x25, 0x52, 0x61, 0x66, 0x61,
0x79, 0x5c, 0x44, 0x65, 0x76, 0x5c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x43, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x5c, 0x56, 0x33, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x61, 0xea, 0x02, 0x1d, 0x52, 0x61, 0x66, 0x61, 0x79, 0x3a, 0x3a, 0x44, 0x65, 0x76, 0x3a, 0x3a,
0x54, 0x79, 0x70, 0x65, 0x73, 0x3a, 0x3a, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3a, 0x3a, 0x56,
0x33, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x55, 0x52, 0x4c, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x73, 0x52, 0x05, 0x76,
0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3d, 0x0a, 0x0f, 0x49, 0x73, 0x4f, 0x72,
0x67, 0x41, 0x64, 0x6d, 0x69, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b,
0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x40, 0x0a, 0x12, 0x49, 0x73, 0x41, 0x6c, 0x6c,
0x4e, 0x73, 0x41, 0x63, 0x63, 0x65, 0x73, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a,
0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05,
0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe, 0x01, 0x0a, 0x18, 0x49, 0x73,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64,
0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e,
0x76, 0x33, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73,
0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x12, 0x48, 0x0a, 0x0b, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x18,
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x72, 0x61, 0x66, 0x61, 0x79, 0x2e, 0x64, 0x65,
0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x76,
0x33, 0x2e, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x52, 0x0b, 0x73,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x44, 0x61, 0x74, 0x61, 0x2a, 0x6f, 0x0a, 0x0d, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0b, 0x0a, 0x07, 0x55,
0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x10, 0x00, 0x12, 0x1b, 0x0a, 0x17, 0x52, 0x65, 0x71, 0x75,
0x65, 0x73, 0x74, 0x4e, 0x6f, 0x74, 0x41, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61,
0x74, 0x65, 0x64, 0x10, 0x01, 0x12, 0x20, 0x0a, 0x1c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x4f, 0x72, 0x55, 0x52, 0x4c, 0x4e, 0x6f, 0x74, 0x41, 0x6c,
0x6c, 0x6f, 0x77, 0x65, 0x64, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x41, 0x6c, 0x6c, 0x6f, 0x77, 0x65, 0x64, 0x10, 0x03, 0x2a, 0x3c, 0x0a, 0x08, 0x41,
0x75, 0x74, 0x68, 0x54, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x0e, 0x41, 0x75, 0x74, 0x68, 0x54,
0x79, 0x70, 0x65, 0x4e, 0x6f, 0x74, 0x53, 0x65, 0x74, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x53,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x10, 0x01, 0x12, 0x0a, 0x0a,
0x06, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x10, 0x02, 0x2a, 0x38, 0x0a, 0x0a, 0x43, 0x6c, 0x69,
0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x10, 0x43, 0x6c, 0x69, 0x65, 0x6e,
0x74, 0x54, 0x79, 0x70, 0x65, 0x4e, 0x6f, 0x74, 0x53, 0x65, 0x74, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x07, 0x42, 0x52, 0x4f, 0x57, 0x53, 0x45, 0x52, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x43, 0x4c,
0x49, 0x10, 0x02, 0x42, 0xf6, 0x01, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x72, 0x61, 0x66, 0x61,
0x79, 0x2e, 0x64, 0x65, 0x76, 0x2e, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x6d,
0x6f, 0x6e, 0x2e, 0x76, 0x33, 0x42, 0x09, 0x41, 0x75, 0x74, 0x68, 0x50, 0x72, 0x6f, 0x74, 0x6f,
0x50, 0x01, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x52,
0x61, 0x66, 0x61, 0x79, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x72, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2d,
0x62, 0x61, 0x73, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73,
0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x76, 0x33, 0x3b, 0x63, 0x6f, 0x6d,
0x6d, 0x6f, 0x6e, 0x76, 0x33, 0xa2, 0x02, 0x04, 0x52, 0x44, 0x54, 0x43, 0xaa, 0x02, 0x19, 0x52,
0x61, 0x66, 0x61, 0x79, 0x2e, 0x44, 0x65, 0x76, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x43,
0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x56, 0x33, 0xca, 0x02, 0x19, 0x52, 0x61, 0x66, 0x61, 0x79,
0x5c, 0x44, 0x65, 0x76, 0x5c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f,
0x6e, 0x5c, 0x56, 0x33, 0xe2, 0x02, 0x25, 0x52, 0x61, 0x66, 0x61, 0x79, 0x5c, 0x44, 0x65, 0x76,
0x5c, 0x54, 0x79, 0x70, 0x65, 0x73, 0x5c, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x5c, 0x56, 0x33,
0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1d, 0x52,
0x61, 0x66, 0x61, 0x79, 0x3a, 0x3a, 0x44, 0x65, 0x76, 0x3a, 0x3a, 0x54, 0x79, 0x70, 0x65, 0x73,
0x3a, 0x3a, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x3a, 0x3a, 0x56, 0x33, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
+6 -3
View File
@@ -70,9 +70,12 @@ message SessionData {
string idp = 15;
map<string, bool> is_org_admin = 16;
ClientType client_type = 17;
map<string, bool> is_all_ns_access = 18;
repeated NamespaceData namespaces = 19;
ProjectData project = 20;
string client_host = 18;
string client_ip = 19;
string client_ua = 20;
map<string, bool> is_all_ns_access = 21;
repeated NamespaceData namespaces = 22;
ProjectData project = 23;
}
message IsRequestAllowedResponse {
+20 -8
View File
@@ -15,6 +15,7 @@ import (
"github.com/RafayLabs/rcloud-base/internal/dao"
"github.com/RafayLabs/rcloud-base/internal/models"
providers "github.com/RafayLabs/rcloud-base/internal/provider/kratos"
"github.com/RafayLabs/rcloud-base/pkg/audit"
"github.com/RafayLabs/rcloud-base/pkg/common"
"github.com/RafayLabs/rcloud-base/pkg/enforcer"
"github.com/RafayLabs/rcloud-base/pkg/service"
@@ -51,6 +52,7 @@ const (
dbUserEnv = "DB_USER"
dbPasswordEnv = "DB_PASSWORD"
kratosAddrEnv = "KRATOS_ADDR"
auditFileEnv = "AUDIT_LOG_FILE"
)
func addResourcePermissions(db *bun.DB, basePath string) error {
@@ -109,7 +111,9 @@ func main() {
viper.SetDefault(dbUserEnv, "admindbuser")
viper.SetDefault(dbPasswordEnv, "admindbpassword")
viper.SetDefault(kratosAddrEnv, "http://localhost:4433")
viper.SetDefault(auditFileEnv, "audit.log")
viper.BindEnv(auditFileEnv)
viper.BindEnv(dbAddrEnv)
viper.BindEnv(dbNameEnv)
viper.BindEnv(dbUserEnv)
@@ -121,6 +125,7 @@ func main() {
dbUser := viper.GetString(dbUserEnv)
dbPassword := viper.GetString(dbPasswordEnv)
kratosAddr := viper.GetString(kratosAddrEnv)
auditFile := viper.GetString(auditFileEnv)
content, err := ioutil.ReadFile(path.Join("scripts", "initialize", "roles", "ztka", "roles.json"))
if err != nil {
@@ -148,6 +153,14 @@ func main() {
kratosConfig.Servers[0].URL = kratosAddr
kc := kclient.NewAPIClient(kratosConfig)
ao := audit.AuditOptions{
LogPath: auditFile,
MaxSizeMB: 1,
MaxBackups: 10, // Should we let sidecar do rotation?
MaxAgeDays: 10, // Make these configurable via env
}
auditLogger := audit.GetAuditLogger(&ao)
// authz services
gormDb, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil {
@@ -159,12 +172,12 @@ func main() {
}
as := service.NewAuthzService(db, enforcer)
ps := service.NewPartnerService(db)
os := service.NewOrganizationService(db)
rs := service.NewRoleService(db, as)
gs := service.NewGroupService(db, as)
us := service.NewUserService(providers.NewKratosAuthProvider(kc), db, as, nil, common.CliConfigDownloadData{})
prs := service.NewProjectService(db, as)
ps := service.NewPartnerService(db, auditLogger)
os := service.NewOrganizationService(db, auditLogger)
rs := service.NewRoleService(db, as, auditLogger)
gs := service.NewGroupService(db, as, auditLogger)
us := service.NewUserService(providers.NewKratosAuthProvider(kc), db, as, nil, common.CliConfigDownloadData{}, auditLogger)
prs := service.NewProjectService(db, as, auditLogger)
//check if there are role permissions already present
existingPermissions := &[]models.ResourceRolePermission{}
@@ -287,6 +300,5 @@ retry:
goto retry
}
fmt.Println("Org Admin signup URL: ", orgA.Spec.RecoveryUrl)
fmt.Println("Org Admin signup URL: ", *orgA.Spec.RecoveryUrl)
}
+1 -2
View File
@@ -5,7 +5,6 @@ import (
"encoding/json"
"fmt"
"github.com/RafayLabs/rcloud-base/pkg/common"
"github.com/RafayLabs/rcloud-base/pkg/query"
"github.com/RafayLabs/rcloud-base/pkg/service"
rpcv3 "github.com/RafayLabs/rcloud-base/proto/rpc/user"
@@ -75,7 +74,7 @@ func (s *userServer) UpdateUser(ctx context.Context, req *userpbv3.User) (*userp
}
func (s *userServer) DownloadCliConfig(ctx context.Context, req *rpcv3.CliConfigRequest) (*commonv3.HttpBody, error) {
sessData, ok := ctx.Value(common.SessionDataKey).(*commonv3.SessionData)
sessData, ok := service.GetSessionDataFromContext(ctx)
if !ok {
return nil, fmt.Errorf("unable to retrieve session data")
}