mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-14 22:20:31 +01:00
Compare commits
8 Commits
main
...
platform-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff54bee061 | ||
|
|
c18c9c42c6 | ||
|
|
c2d1e07604 | ||
|
|
8aa3864b8d | ||
|
|
e1144b8e37 | ||
|
|
3f76fe7d81 | ||
|
|
9eb8dcfd94 | ||
|
|
d7e6e5a9f6 |
53
cmd/authz.go
53
cmd/authz.go
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
@@ -23,6 +24,7 @@ export default {
|
||||
{
|
||||
kind: '{{ .Kind }}',
|
||||
type: '{{ .Type }}',
|
||||
{{ .FormattedAllowedVerbs }}
|
||||
},
|
||||
{{- end }}
|
||||
],
|
||||
@@ -41,8 +43,9 @@ type permissionsTypeRelation struct {
|
||||
}
|
||||
|
||||
type permissionsTypeResource struct {
|
||||
Kind string
|
||||
Type string
|
||||
Kind string
|
||||
Type string
|
||||
FormattedAllowedVerbs string
|
||||
}
|
||||
|
||||
type permissionsTypeData struct {
|
||||
@@ -50,6 +53,30 @@ type permissionsTypeData struct {
|
||||
Relations []permissionsTypeRelation
|
||||
}
|
||||
|
||||
// formatAllowedVerbs returns a prettier-compatible formatted allowedVerbs line.
|
||||
// indentLevel is the number of tabs for the property (matching kind/type indent).
|
||||
// printWidth is prettier's printWidth; tabWidth is assumed to be 1 (each \t = 1 char).
|
||||
func formatAllowedVerbs(verbs []string, indentLevel int, printWidth int) string {
|
||||
quoted := make([]string, len(verbs))
|
||||
for i, v := range verbs {
|
||||
quoted[i] = "'" + v + "'"
|
||||
}
|
||||
indent := strings.Repeat("\t", indentLevel)
|
||||
|
||||
oneLine := indent + "allowedVerbs: [" + strings.Join(quoted, ", ") + "],"
|
||||
if len(oneLine) <= printWidth {
|
||||
return oneLine
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString(indent + "allowedVerbs: [\n")
|
||||
for _, q := range quoted {
|
||||
b.WriteString(indent + "\t" + q + ",\n")
|
||||
}
|
||||
b.WriteString(indent + "],")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func registerGenerateAuthz(parentCmd *cobra.Command) {
|
||||
authzCmd := &cobra.Command{
|
||||
Use: "authz",
|
||||
@@ -66,8 +93,8 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
registry := coretypes.NewRegistry()
|
||||
|
||||
allowedResources := map[string]bool{
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceFactorAPIKey).String(): true,
|
||||
}
|
||||
|
||||
@@ -80,9 +107,23 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
continue
|
||||
}
|
||||
allowedTypes[ref.Type.StringValue()] = true
|
||||
|
||||
resource, err := coretypes.NewResourceFromTypeAndKind(ref.Type, ref.Kind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
verbs := resource.AllowedVerbs()
|
||||
allowedVerbStrings := make([]string, 0, len(verbs))
|
||||
for _, verb := range verbs {
|
||||
allowedVerbStrings = append(allowedVerbStrings, verb.StringValue())
|
||||
}
|
||||
sort.Strings(allowedVerbStrings)
|
||||
|
||||
resources = append(resources, permissionsTypeResource{
|
||||
Kind: ref.Kind.String(),
|
||||
Type: ref.Type.StringValue(),
|
||||
Kind: ref.Kind.String(),
|
||||
Type: ref.Type.StringValue(),
|
||||
FormattedAllowedVerbs: formatAllowedVerbs(allowedVerbStrings, 4, 80),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,9 @@ type metaresource
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
define block: [user, serviceaccount, role#assignee]
|
||||
|
||||
|
||||
|
||||
@@ -1,65 +1,16 @@
|
||||
import { Callout } from '@signozhq/ui/callout';
|
||||
import ClickHouseQueryBuilder from 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/query';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { AlertTypes } from 'types/api/alerts/alertTypes';
|
||||
import DOCLINKS from 'utils/docLinks';
|
||||
|
||||
import 'container/NewWidget/LeftContainer/QuerySection/QueryBuilder/ClickHouse/ClickHouse.styles.scss';
|
||||
|
||||
const ALERT_TYPE_DOC_LINK: Partial<Record<AlertTypes, string>> = {
|
||||
[AlertTypes.LOGS_BASED_ALERT]: DOCLINKS.QUERY_CLICKHOUSE_LOGS,
|
||||
[AlertTypes.TRACES_BASED_ALERT]: DOCLINKS.QUERY_CLICKHOUSE_TRACES,
|
||||
[AlertTypes.EXCEPTIONS_BASED_ALERT]: DOCLINKS.QUERY_CLICKHOUSE_TRACES,
|
||||
[AlertTypes.METRICS_BASED_ALERT]: DOCLINKS.QUERY_CLICKHOUSE_METRICS,
|
||||
};
|
||||
|
||||
const ALERT_TYPES_WITH_AGENT_SKILL: AlertTypes[] = [
|
||||
AlertTypes.LOGS_BASED_ALERT,
|
||||
AlertTypes.TRACES_BASED_ALERT,
|
||||
AlertTypes.EXCEPTIONS_BASED_ALERT,
|
||||
];
|
||||
|
||||
interface ChQuerySectionProps {
|
||||
alertType: AlertTypes;
|
||||
}
|
||||
|
||||
function ChQuerySection({ alertType }: ChQuerySectionProps): JSX.Element {
|
||||
function ChQuerySection(): JSX.Element {
|
||||
const { currentQuery } = useQueryBuilder();
|
||||
const docLink = ALERT_TYPE_DOC_LINK[alertType];
|
||||
const showAgentSkill = ALERT_TYPES_WITH_AGENT_SKILL.includes(alertType);
|
||||
|
||||
return (
|
||||
<>
|
||||
{docLink && (
|
||||
<div className="info-banner-wrapper">
|
||||
<Callout
|
||||
type="info"
|
||||
showIcon
|
||||
title={
|
||||
<span>
|
||||
<a href={docLink} target="_blank" rel="noopener">
|
||||
Learn to write faster, optimized queries
|
||||
</a>
|
||||
{showAgentSkill && (
|
||||
<>
|
||||
{' · Using AI? '}
|
||||
<a href={DOCLINKS.AGENT_SKILL_INSTALL} target="_blank" rel="noopener">
|
||||
Install the SigNoz ClickHouse query agent skill
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<ClickHouseQueryBuilder
|
||||
key="A"
|
||||
queryIndex={0}
|
||||
queryData={currentQuery.clickhouse_sql[0]}
|
||||
deletable={false}
|
||||
/>
|
||||
</>
|
||||
<ClickHouseQueryBuilder
|
||||
key="A"
|
||||
queryIndex={0}
|
||||
queryData={currentQuery.clickhouse_sql[0]}
|
||||
deletable={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,9 +56,7 @@ function QuerySection({
|
||||
|
||||
const renderPromqlUI = (): JSX.Element => <PromqlSection />;
|
||||
|
||||
const renderChQueryUI = (): JSX.Element => (
|
||||
<ChQuerySection alertType={alertType} />
|
||||
);
|
||||
const renderChQueryUI = (): JSX.Element => <ChQuerySection />;
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
|
||||
@@ -26,12 +26,12 @@ function ClickHouseQueryContainer(): JSX.Element | null {
|
||||
<a
|
||||
href={DOCLINKS.QUERY_CLICKHOUSE_TRACES}
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn to write faster, optimized queries
|
||||
</a>
|
||||
{' · Using AI? '}
|
||||
<a href={DOCLINKS.AGENT_SKILL_INSTALL} target="_blank" rel="noopener">
|
||||
<a href={DOCLINKS.AGENT_SKILL_INSTALL} target="_blank" rel="noreferrer">
|
||||
Install the SigNoz ClickHouse query agent skill
|
||||
</a>
|
||||
</span>
|
||||
|
||||
@@ -118,10 +118,14 @@ export function buildPatchPayload({
|
||||
for (const res of resources) {
|
||||
const initial = initialConfig[res.id];
|
||||
const current = newConfig[res.id];
|
||||
const resourceDef = authzRes.resources.find((r) => r.kind === res.id);
|
||||
if (!resourceDef) {
|
||||
const found = authzRes.resources.find((r) => r.kind === res.id);
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
const resourceDef: CoretypesResourceRefDTO = {
|
||||
kind: found.kind,
|
||||
type: found.type,
|
||||
};
|
||||
|
||||
const initialScope = initial?.scope ?? PermissionScope.ONLY_SELECTED;
|
||||
const currentScope = current?.scope ?? PermissionScope.ONLY_SELECTED;
|
||||
|
||||
@@ -6,14 +6,34 @@ export default {
|
||||
{
|
||||
kind: 'factor-api-key',
|
||||
type: 'metaresource',
|
||||
allowedVerbs: ['create', 'delete', 'list', 'read', 'update'],
|
||||
},
|
||||
{
|
||||
kind: 'role',
|
||||
type: 'role',
|
||||
allowedVerbs: [
|
||||
'assignee',
|
||||
'attach',
|
||||
'create',
|
||||
'delete',
|
||||
'detach',
|
||||
'list',
|
||||
'read',
|
||||
'update',
|
||||
],
|
||||
},
|
||||
{
|
||||
kind: 'serviceaccount',
|
||||
type: 'serviceaccount',
|
||||
allowedVerbs: [
|
||||
'attach',
|
||||
'create',
|
||||
'delete',
|
||||
'detach',
|
||||
'list',
|
||||
'read',
|
||||
'update',
|
||||
],
|
||||
},
|
||||
],
|
||||
relations: {
|
||||
|
||||
@@ -10,10 +10,6 @@ const DOCLINKS = {
|
||||
'https://signoz.io/docs/external-api-monitoring/overview/',
|
||||
QUERY_CLICKHOUSE_TRACES:
|
||||
'https://signoz.io/docs/userguide/writing-clickhouse-traces-query/#timestamp-bucketing-for-distributed_signoz_index_v3',
|
||||
QUERY_CLICKHOUSE_LOGS:
|
||||
'https://signoz.io/docs/userguide/logs_clickhouse_queries/',
|
||||
QUERY_CLICKHOUSE_METRICS:
|
||||
'https://signoz.io/docs/userguide/write-a-metrics-clickhouse-query/',
|
||||
AGENT_SKILL_INSTALL: 'https://signoz.io/docs/ai/agent-skills/#installation',
|
||||
};
|
||||
|
||||
|
||||
@@ -202,6 +202,7 @@ func NewSQLMigrationProviderFactories(
|
||||
sqlmigration.NewAddLLMPricingRulesFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewMigrateMetaresourcesTuplesFactory(sqlstore),
|
||||
sqlmigration.NewAddTagsFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddRoleCRUDTuplesFactory(sqlstore),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
139
pkg/sqlmigration/083_add_role_crud_tuples.go
Normal file
139
pkg/sqlmigration/083_add_role_crud_tuples.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package sqlmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
)
|
||||
|
||||
type addRoleCRUDTuples struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewAddRoleCRUDTuplesFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("add_role_crud_tuples"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||
return &addRoleCRUDTuples{sqlstore: sqlstore}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (migration *addRoleCRUDTuples) Register(migrations *migrate.Migrations) error {
|
||||
return migrations.Register(migration.Up, migration.Down)
|
||||
}
|
||||
|
||||
func (migration *addRoleCRUDTuples) Up(ctx context.Context, db *bun.DB) error {
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
var storeID string
|
||||
err = tx.QueryRowContext(ctx, `SELECT id FROM store WHERE name = ? LIMIT 1`, "signoz").Scan(&storeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var orgIDs []string
|
||||
rows, err := tx.QueryContext(ctx, `SELECT id FROM organizations`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var orgID string
|
||||
if err := rows.Scan(&orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
orgIDs = append(orgIDs, orgID)
|
||||
}
|
||||
|
||||
isPG := migration.sqlstore.BunDB().Dialect().Name() == dialect.PG
|
||||
|
||||
// Migration 081 moved role tuples from "metaresources" to "role" type but
|
||||
// only inserted create and list. The read, update, and delete tuples were
|
||||
// lost in the migration. Re-add them here.
|
||||
tuples := []migrationTuple{
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "read"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "update"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "delete"},
|
||||
}
|
||||
|
||||
for _, orgID := range orgIDs {
|
||||
for _, tuple := range tuples {
|
||||
entropy := ulid.DefaultEntropy()
|
||||
now := time.Now().UTC()
|
||||
tupleID := ulid.MustNew(ulid.Timestamp(now), entropy).String()
|
||||
|
||||
objectID := "organization/" + orgID + "/" + tuple.objectName + "/*"
|
||||
roleSubject := "organization/" + orgID + "/role/" + tuple.roleName
|
||||
|
||||
if isPG {
|
||||
user := "role:" + roleSubject + "#assignee"
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, _user, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, _user) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, _user, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "TUPLE_OPERATION_WRITE", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", 0, tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (migration *addRoleCRUDTuples) Down(context.Context, *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
@@ -25,7 +25,7 @@ type TransactionWithAuthorization struct {
|
||||
}
|
||||
|
||||
func NewTransaction(relation Relation, object coretypes.Object) (*Transaction, error) {
|
||||
if err := coretypes.ErrIfVerbNotValidForType(relation.Verb, object.Resource.Type); err != nil {
|
||||
if err := coretypes.ErrIfVerbNotValidForResource(relation.Verb, object.Resource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -129,13 +129,13 @@ func NewPatchableObjects(additions []*ObjectGroup, deletions []*ObjectGroup, ver
|
||||
}
|
||||
|
||||
for _, objectGroup := range additions {
|
||||
if err := ErrIfVerbNotValidForType(verb, objectGroup.Resource.Type); err != nil {
|
||||
if err := ErrIfVerbNotValidForResource(verb, objectGroup.Resource); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
for _, objectGroup := range deletions {
|
||||
if err := ErrIfVerbNotValidForType(verb, objectGroup.Resource.Type); err != nil {
|
||||
if err := ErrIfVerbNotValidForResource(verb, objectGroup.Resource); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ var (
|
||||
ResourceMetaResourceSavedView = NewResourceMetaResource(KindSavedView)
|
||||
ResourceMetaResourceTraceFunnel = NewResourceMetaResource(KindTraceFunnel)
|
||||
ResourceMetaResourceFactorPassword = NewResourceMetaResource(KindFactorPassword)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey, VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete)
|
||||
ResourceMetaResourceLicense = NewResourceMetaResource(KindLicense)
|
||||
ResourceMetaResourceSubscription = NewResourceMetaResource(KindSubscription)
|
||||
ResourceTelemetryResourceLogs = NewResourceTelemetryResource(KindLogs)
|
||||
|
||||
@@ -21,7 +21,7 @@ var (
|
||||
TypeServiceAccount = Type{valuer.NewString("serviceaccount"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeAnonymous = Type{valuer.NewString("anonymous"), regexp.MustCompile(`^\*$`), []Verb{}}
|
||||
TypeRole = Type{valuer.NewString("role"), regexp.MustCompile(`^([a-z-]{1,50}|\*)$`), []Verb{VerbAssignee, VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeOrganization = Type{valuer.NewString("organization"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeOrganization = Type{valuer.NewString("organization"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate}}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeTelemetryResource = Type{valuer.NewString("telemetryresource"), regexp.MustCompile(`^\*$`), []Verb{VerbRead}}
|
||||
)
|
||||
|
||||
@@ -19,6 +19,12 @@ type Resource interface {
|
||||
|
||||
// Scope of the resource.
|
||||
Scope(verb Verb) string
|
||||
|
||||
// AllowedVerbs returns the verbs that are valid for this resource.
|
||||
// By default, this delegates to the type's allowed verbs, but specific
|
||||
// resources can restrict the set further (e.g., some metaresource kinds
|
||||
// may not support attach/detach).
|
||||
AllowedVerbs() []Verb
|
||||
}
|
||||
|
||||
type ResourceRef struct {
|
||||
|
||||
@@ -34,3 +34,7 @@ func (resourceAnonymous *resourceAnonymous) Object(orgID valuer.UUID, selector s
|
||||
func (resourceAnonymous *resourceAnonymous) Scope(verb Verb) string {
|
||||
return resourceAnonymous.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceAnonymous) AllowedVerbs() []Verb {
|
||||
return TypeAnonymous.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -5,11 +5,15 @@ import (
|
||||
)
|
||||
|
||||
type resourceMetaResource struct {
|
||||
kind Kind
|
||||
kind Kind
|
||||
allowedVerbs []Verb
|
||||
}
|
||||
|
||||
func NewResourceMetaResource(kind Kind) Resource {
|
||||
return &resourceMetaResource{kind: kind}
|
||||
func NewResourceMetaResource(kind Kind, allowedVerbs ...Verb) Resource {
|
||||
if len(allowedVerbs) == 0 {
|
||||
allowedVerbs = TypeMetaResource.AllowedVerbs()
|
||||
}
|
||||
return &resourceMetaResource{kind: kind, allowedVerbs: allowedVerbs}
|
||||
}
|
||||
|
||||
func (*resourceMetaResource) Type() Type {
|
||||
@@ -32,3 +36,7 @@ func (resourceMetaResource *resourceMetaResource) Object(orgID valuer.UUID, sele
|
||||
func (resourceMetaResource *resourceMetaResource) Scope(verb Verb) string {
|
||||
return resourceMetaResource.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (resourceMetaResource *resourceMetaResource) AllowedVerbs() []Verb {
|
||||
return resourceMetaResource.allowedVerbs
|
||||
}
|
||||
|
||||
@@ -33,3 +33,7 @@ func (resourceOrganization *resourceOrganization) Object(orgID valuer.UUID, sele
|
||||
func (resourceOrganization *resourceOrganization) Scope(verb Verb) string {
|
||||
return resourceOrganization.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceOrganization) AllowedVerbs() []Verb {
|
||||
return TypeOrganization.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -34,3 +34,7 @@ func (resourceRole *resourceRole) Object(orgID valuer.UUID, selector string) str
|
||||
func (resourceRole *resourceRole) Scope(verb Verb) string {
|
||||
return resourceRole.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceRole) AllowedVerbs() []Verb {
|
||||
return TypeRole.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -34,3 +34,7 @@ func (resourceServiceAccount *resourceServiceAccount) Object(orgID valuer.UUID,
|
||||
func (resourceServiceAccount *resourceServiceAccount) Scope(verb Verb) string {
|
||||
return resourceServiceAccount.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceServiceAccount) AllowedVerbs() []Verb {
|
||||
return TypeServiceAccount.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -32,3 +32,7 @@ func (resourceTelemetryResource *resourceTelemetryResource) Object(orgID valuer.
|
||||
func (resourceTelemetryResource *resourceTelemetryResource) Scope(verb Verb) string {
|
||||
return resourceTelemetryResource.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceTelemetryResource) AllowedVerbs() []Verb {
|
||||
return TypeTelemetryResource.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -34,3 +34,7 @@ func (resourceUser *resourceUser) Object(orgID valuer.UUID, selector string) str
|
||||
func (resourceUser *resourceUser) Scope(verb Verb) string {
|
||||
return resourceUser.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
|
||||
func (*resourceUser) AllowedVerbs() []Verb {
|
||||
return TypeUser.AllowedVerbs()
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ type Transaction struct {
|
||||
}
|
||||
|
||||
func NewTransaction(verb Verb, object Object) (*Transaction, error) {
|
||||
if err := ErrIfVerbNotValidForType(verb, object.Resource.Type); err != nil {
|
||||
if err := ErrIfVerbNotValidForResource(verb, object.Resource); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,25 @@ func ErrIfVerbNotValidForType(verb Verb, typed Type) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ErrIfVerbNotValidForResource(verb Verb, ref ResourceRef) error {
|
||||
if err := ErrIfVerbNotValidForType(verb, ref.Type); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resource, err := NewResourceFromTypeAndKind(ref.Type, ref.Kind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, allowed := range resource.AllowedVerbs() {
|
||||
if verb == allowed {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidVerbForType, "verb %s is not valid for resource %s:%s", verb.StringValue(), ref.Type.StringValue(), ref.Kind.String())
|
||||
}
|
||||
|
||||
func (typed *Type) UnmarshalJSON(data []byte) error {
|
||||
str := ""
|
||||
err := json.Unmarshal(data, &str)
|
||||
|
||||
Reference in New Issue
Block a user