summaryrefslogtreecommitdiff
path: root/vendor/github.com/authzed/spicedb/internal/services/v1/hash.go
blob: 1754669e922b43ed4441b9f04a81a02ae5e80277 (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
package v1

import (
	"strconv"

	v1 "github.com/authzed/authzed-go/proto/authzed/api/v1"
	"google.golang.org/protobuf/types/known/structpb"

	"github.com/authzed/spicedb/pkg/caveats"
	"github.com/authzed/spicedb/pkg/spiceerrors"
	"github.com/authzed/spicedb/pkg/tuple"
)

func computeCheckBulkPermissionsItemHashWithoutResourceID(req *v1.CheckBulkPermissionsRequestItem) (string, error) {
	return computeCallHash("v1.checkbulkpermissionsrequestitem", nil, map[string]any{
		"resource-type":    req.Resource.ObjectType,
		"permission":       req.Permission,
		"subject-type":     req.Subject.Object.ObjectType,
		"subject-id":       req.Subject.Object.ObjectId,
		"subject-relation": req.Subject.OptionalRelation,
		"context":          req.Context,
	})
}

func computeCheckBulkPermissionsItemHash(req *v1.CheckBulkPermissionsRequestItem) (string, error) {
	return computeCallHash("v1.checkbulkpermissionsrequestitem", nil, map[string]any{
		"resource-type":    req.Resource.ObjectType,
		"resource-id":      req.Resource.ObjectId,
		"permission":       req.Permission,
		"subject-type":     req.Subject.Object.ObjectType,
		"subject-id":       req.Subject.Object.ObjectId,
		"subject-relation": req.Subject.OptionalRelation,
		"context":          req.Context,
	})
}

func computeReadRelationshipsRequestHash(req *v1.ReadRelationshipsRequest) (string, error) {
	osf := req.RelationshipFilter.OptionalSubjectFilter
	if osf == nil {
		osf = &v1.SubjectFilter{}
	}

	srf := "(none)"
	if osf.OptionalRelation != nil {
		srf = osf.OptionalRelation.Relation
	}

	return computeCallHash("v1.readrelationships", req.Consistency, map[string]any{
		"filter-resource-type": req.RelationshipFilter.ResourceType,
		"filter-relation":      req.RelationshipFilter.OptionalRelation,
		"filter-resource-id":   req.RelationshipFilter.OptionalResourceId,
		"subject-type":         osf.SubjectType,
		"subject-relation":     srf,
		"subject-resource-id":  osf.OptionalSubjectId,
		"limit":                req.OptionalLimit,
	})
}

func computeLRRequestHash(req *v1.LookupResourcesRequest) (string, error) {
	return computeCallHash("v1.lookupresources", req.Consistency, map[string]any{
		"resource-type": req.ResourceObjectType,
		"permission":    req.Permission,
		"subject":       tuple.V1StringSubjectRef(req.Subject),
		"limit":         req.OptionalLimit,
		"context":       req.Context,
	})
}

func computeCallHash(apiName string, consistency *v1.Consistency, arguments map[string]any) (string, error) {
	stringArguments := make(map[string]string, len(arguments)+1)

	if consistency == nil {
		consistency = &v1.Consistency{
			Requirement: &v1.Consistency_MinimizeLatency{
				MinimizeLatency: true,
			},
		}
	}

	consistencyBytes, err := consistency.MarshalVT()
	if err != nil {
		return "", err
	}

	stringArguments["consistency"] = string(consistencyBytes)

	for argName, argValue := range arguments {
		if argName == "consistency" {
			return "", spiceerrors.MustBugf("cannot specify consistency in the arguments")
		}

		switch v := argValue.(type) {
		case string:
			stringArguments[argName] = v

		case int:
			stringArguments[argName] = strconv.Itoa(v)

		case uint32:
			stringArguments[argName] = strconv.Itoa(int(v))

		case *structpb.Struct:
			stringArguments[argName] = caveats.StableContextStringForHashing(v)

		default:
			return "", spiceerrors.MustBugf("unknown argument type in compute call hash")
		}
	}
	return computeAPICallHash(apiName, stringArguments)
}