summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/pkg/tuple/structs.go
blob: e6cd2560971897a62b54a72a4f218fcd2f851158 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package tuple

import (
	"errors"
	"fmt"
	"time"

	"google.golang.org/protobuf/types/known/timestamppb"

	core "github.com/authzed/spicedb/pkg/proto/core/v1"
	"github.com/authzed/spicedb/pkg/spiceerrors"
)

const (
	// Ellipsis is the Ellipsis relation in v0 style subjects.
	Ellipsis = "..."

	// PublicWildcard is the wildcard value for subject object IDs that indicates public access
	// for the subject type.
	PublicWildcard = "*"
)

// ObjectAndRelation represents an object and its relation.
type ObjectAndRelation struct {
	ObjectID   string
	ObjectType string
	Relation   string
}

const onrStructSize = 48 /* size of the struct itself */

func (onr ObjectAndRelation) SizeVT() int {
	return len(onr.ObjectID) + len(onr.ObjectType) + len(onr.Relation) + onrStructSize
}

// WithRelation returns a copy of the object and relation with the given relation.
func (onr ObjectAndRelation) WithRelation(relation string) ObjectAndRelation {
	onr.Relation = relation
	return onr
}

// RelationReference returns a RelationReference for the object and relation.
func (onr ObjectAndRelation) RelationReference() RelationReference {
	return RelationReference{
		ObjectType: onr.ObjectType,
		Relation:   onr.Relation,
	}
}

// ToCoreONR converts the ObjectAndRelation to a core.ObjectAndRelation.
func (onr ObjectAndRelation) ToCoreONR() *core.ObjectAndRelation {
	return &core.ObjectAndRelation{
		Namespace: onr.ObjectType,
		ObjectId:  onr.ObjectID,
		Relation:  onr.Relation,
	}
}

func (onr ObjectAndRelation) String() string {
	return fmt.Sprintf("%s:%s#%s", onr.ObjectType, onr.ObjectID, onr.Relation)
}

// RelationshipReference represents a reference to a relationship, i.e. those portions
// of a relationship that are not the integrity or caveat and thus form the unique
// identifier of the relationship.
type RelationshipReference struct {
	Resource ObjectAndRelation
	Subject  ObjectAndRelation
}

// Relationship represents a relationship between two objects.
type Relationship struct {
	OptionalCaveat     *core.ContextualizedCaveat
	OptionalExpiration *time.Time
	OptionalIntegrity  *core.RelationshipIntegrity

	RelationshipReference
}

// ToCoreTuple converts the Relationship to a core.RelationTuple.
func (r Relationship) ToCoreTuple() *core.RelationTuple {
	var expirationTime *timestamppb.Timestamp
	if r.OptionalExpiration != nil {
		expirationTime = timestamppb.New(*r.OptionalExpiration)
	}

	return &core.RelationTuple{
		ResourceAndRelation:    r.Resource.ToCoreONR(),
		Subject:                r.Subject.ToCoreONR(),
		Caveat:                 r.OptionalCaveat,
		Integrity:              r.OptionalIntegrity,
		OptionalExpirationTime: expirationTime,
	}
}

const relStructSize = 120 /* size of the struct itself */

func (r Relationship) SizeVT() int {
	size := r.Resource.SizeVT() + r.Subject.SizeVT() + relStructSize
	if r.OptionalCaveat != nil {
		size += r.OptionalCaveat.SizeVT()
	}
	return size
}

// ValidateNotEmpty returns true if the relationship is not empty.
func (r Relationship) ValidateNotEmpty() bool {
	return r.Resource.ObjectType != "" && r.Resource.ObjectID != "" && r.Subject.ObjectType != "" && r.Subject.ObjectID != "" && r.Resource.Relation != "" && r.Subject.Relation != ""
}

// Validate returns an error if the relationship is invalid.
func (r Relationship) Validate() error {
	if !r.ValidateNotEmpty() {
		return errors.New("object and relation must not be empty")
	}

	if r.RelationshipReference.Resource.ObjectID == PublicWildcard {
		return errors.New("invalid resource id")
	}

	return nil
}

// WithoutIntegrity returns a copy of the relationship without its integrity.
func (r Relationship) WithoutIntegrity() Relationship {
	r.OptionalIntegrity = nil
	return r
}

// WithCaveat returns a copy of the relationship with the given caveat.
func (r Relationship) WithCaveat(caveat *core.ContextualizedCaveat) Relationship {
	r.OptionalCaveat = caveat
	return r
}

// UpdateOperation represents the type of update to a relationship.
type UpdateOperation int

const (
	UpdateOperationTouch UpdateOperation = iota
	UpdateOperationCreate
	UpdateOperationDelete
)

// RelationshipUpdate represents an update to a relationship.
type RelationshipUpdate struct {
	Relationship Relationship
	Operation    UpdateOperation
}

func (ru RelationshipUpdate) OperationString() string {
	switch ru.Operation {
	case UpdateOperationTouch:
		return "TOUCH"
	case UpdateOperationCreate:
		return "CREATE"
	case UpdateOperationDelete:
		return "DELETE"
	default:
		return "unknown"
	}
}

func (ru RelationshipUpdate) DebugString() string {
	return fmt.Sprintf("%s(%s)", ru.OperationString(), StringWithoutCaveatOrExpiration(ru.Relationship))
}

// RelationReference represents a reference to a relation.
type RelationReference struct {
	ObjectType string
	Relation   string
}

// ToCoreRR converts the RelationReference to a core.RelationReference.
func (rr RelationReference) ToCoreRR() *core.RelationReference {
	return &core.RelationReference{
		Namespace: rr.ObjectType,
		Relation:  rr.Relation,
	}
}

func (rr RelationReference) RefString() string {
	return JoinRelRef(rr.ObjectType, rr.Relation)
}

func (rr RelationReference) String() string {
	return rr.RefString()
}

// ONR creates an ObjectAndRelation.
func ONR(namespace, objectID, relation string) ObjectAndRelation {
	spiceerrors.DebugAssert(func() bool {
		return namespace != "" && objectID != "" && relation != ""
	}, "invalid ONR: %s %s %s", namespace, objectID, relation)

	return ObjectAndRelation{
		ObjectType: namespace,
		ObjectID:   objectID,
		Relation:   relation,
	}
}

// ONRRef creates an ObjectAndRelation reference.
func ONRRef(namespace, objectID, relation string) *ObjectAndRelation {
	onr := ONR(namespace, objectID, relation)
	return &onr
}

// RR creates a RelationReference.
func RR(namespace, relation string) RelationReference {
	spiceerrors.DebugAssert(func() bool {
		return namespace != "" && relation != ""
	}, "invalid RR: %s %s", namespace, relation)

	return RelationReference{
		ObjectType: namespace,
		Relation:   relation,
	}
}