Skip to content

Commit

Permalink
PR(WIP): Store Policy Key and Register ACP Object
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzadlone committed Jan 27, 2024
1 parent ff7c2f6 commit 9e10f3d
Show file tree
Hide file tree
Showing 10 changed files with 234 additions and 32 deletions.
10 changes: 5 additions & 5 deletions acp/acp.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type ACPModule interface {

// AddPolicy attempts to add/load/create the given policy, if isYAML is false, assumes JSON format,
// upon success a policyID is returned, otherwise returns error.
AddPolicy(ctx context.Context, policy string, creator string, isYAML bool) (string, error)
AddPolicy(ctx context.Context, creator string, policy string, isYAML bool) (string, error)

// ValidatePolicyAndResourceExist returns an error if the policyID does not exist on the
// acp module, or if the resource name is not a valid resource on the target policy,
Expand All @@ -55,16 +55,16 @@ type ACPModule interface {
// Note:
// - This should be used upon document creation only.
// - Some documents might be created without an identity signature so they would have public access.
// - creator here is the actorID, which will be the signature identity if it exists.
// - resource here is the resource object name (likely collection name).
// - docID here is the object identifier.
// - creator here is the actorID, which will be the signature identity if it exists.
RegisterDocCreation(ctx context.Context, policyID, creator, docID, resource string) error
RegisterDocCreation(ctx context.Context, creator, policyID, resource, docID string) error

// CheckDocAccess returns true if request has access to the document, otherwise returns false or an error.
//
// Note:
// - permission here is the type of permission we are checking for ("read" or "write").
// - resource here is the resource object name (likely collection name).
// - docID here is the object identifier.
// - permission here is the type of permission we are checking for ("read" or "write").
CheckDocAccess(ctx context.Context, policyID, actorID, docID, resource, permission string) (bool, error)
CheckDocAccess(ctx context.Context, actorID, permission, policyID, resource, docID string) (bool, error)
}
13 changes: 7 additions & 6 deletions acp/acp_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ func (l *ACPLocalEmbedded) Close() error {

func (l *ACPLocalEmbedded) AddPolicy(
ctx context.Context,
policy string,
creator string,
policy string,
isYAML bool,
) (string, error) {
var createPolicy types.MsgCreatePolicy
Expand Down Expand Up @@ -157,8 +157,8 @@ func (l *ACPLocalEmbedded) ValidatePolicyAndResourceExist(

func (l *ACPLocalEmbedded) RegisterDocCreation(
ctx context.Context,
policyID string,
creator string,
policyID string,
resource string,
docID string,
) error {
Expand All @@ -170,9 +170,10 @@ func (l *ACPLocalEmbedded) RegisterDocCreation(
}

registerDocResponse, err := l.localModule.GetMsgService().RegisterObject(
ctx,
l.localModule.GetCtx(),
&registerDoc,
)

if err != nil {
log.FatalE(
ctx,
Expand Down Expand Up @@ -226,11 +227,11 @@ func (l *ACPLocalEmbedded) RegisterDocCreation(

func (l *ACPLocalEmbedded) CheckDocAccess(
ctx context.Context,
policyID string,
actorID string,
permission string,
policyID string,
resource string,
docID string,
permission string,
) (bool, error) {
checkDoc := types.QueryVerifyAccessRequestRequest{
PolicyId: policyID,
Expand All @@ -248,7 +249,7 @@ func (l *ACPLocalEmbedded) CheckDocAccess(
}

checkDocResponse, err := l.localModule.GetQueryService().VerifyAccessRequest(
ctx,
l.localModule.GetCtx(),
&checkDoc,
)
if err != nil {
Expand Down
20 changes: 20 additions & 0 deletions client/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,12 @@ type Collection interface {
// GetAllDocIDs returns all the document IDs that exist in the collection.
GetAllDocIDs(ctx context.Context) (<-chan DocIDResult, error)

// AddPolicy adds a policy on the collection.
// `PolicyDescription` contains the description of the policy that is added.
// `PolicyDescription.ID` is a policyID that must be a valid policy on the acp module.
// `PolicyDescription.ResourceName` is a valid resource on the target (policyID) policy.
// AddPolicy(context.Context, PolicyDescription) (PolicyDescription, error)

// CreateIndex creates a new index on the collection.
// `IndexDescription` contains the description of the index to be created.
// `IndexDescription.Name` must start with a letter or an underscore and can
Expand All @@ -179,6 +185,20 @@ type Collection interface {
GetIndexes(ctx context.Context) ([]IndexDescription, error)
}

// IsPermissioned returns true if the collection has a policy, otherwise returns false.
//
// This tells us if access control is enabled for this collection or not.
func IsPermissioned(c Collection) (string, string, bool) {
policy := c.Definition().Description.Policy
if policy.HasValue() &&
policy.Value().ID != "" &&
policy.Value().ResourceName != "" {
return policy.Value().ID, policy.Value().ResourceName, true
}

return "", "", false
}

// DocIDResult wraps the result of an attempt at a DocID retrieval operation.
type DocIDResult struct {
// If a DocID was successfully retrieved, this will be that DocID.
Expand Down
57 changes: 56 additions & 1 deletion core/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ const (
COLLECTION_NAME = "/collection/name"
COLLECTION_SCHEMA_VERSION = "/collection/version"
COLLECTION_INDEX = "/collection/index"
COLLECTION_POLICY = "/collection/policy"
SCHEMA_MIGRATION = "/schema/migration"
SCHEMA_VERSION = "/schema/version/v"
SCHEMA_VERSION_HISTORY = "/schema/version/h"
Expand Down Expand Up @@ -131,7 +132,7 @@ type CollectionSchemaVersionKey struct {

var _ Key = (*CollectionSchemaVersionKey)(nil)

// CollectionIndexKey to a stored description of an index
// CollectionIndexKey points to a stored description of an index
type CollectionIndexKey struct {
// CollectionID is the id of the collection that the index is on
CollectionID immutable.Option[uint32]
Expand All @@ -141,6 +142,14 @@ type CollectionIndexKey struct {

var _ Key = (*CollectionIndexKey)(nil)

// CollectionPolicyKey points to the stored policy description of the collection.
type CollectionPolicyKey struct {
// CollectionID is the id of the collection that the policy is on
CollectionID uint32
}

var _ Key = (*CollectionPolicyKey)(nil)

// SchemaVersionKey points to the json serialized schema at the specified version.
//
// It's corresponding value is immutable.
Expand Down Expand Up @@ -291,6 +300,52 @@ func NewCollectionSchemaVersionKeyFromString(key string) (CollectionSchemaVersio
}, nil
}

// NewCollectionPolicyKey creates a new CollectionPolicyKey from a collectionID.
func NewCollectionPolicyKey(
colID uint32,
) CollectionPolicyKey {
return CollectionPolicyKey{
CollectionID: colID,
}
}

// NewCollectionPolicyKeyFromString creates a new CollectionIndexKey from a string.
// It expects the input string in the following format:
//
// /collection/policy/[CollectionID]
//
// Where [CollectionID] must not be omitted.
func NewCollectionPolicyKeyFromString(key string) (CollectionPolicyKey, error) {
keyElements := strings.Split(key, "/")
if len(keyElements) != 4 || keyElements[1] != "collection" || keyElements[2] != "policy" {
return CollectionPolicyKey{}, ErrInvalidKey
}

colID, err := strconv.Atoi(keyElements[3])
if err != nil {
return CollectionPolicyKey{}, err
}

return CollectionPolicyKey{
CollectionID: uint32(colID),
}, nil
}

// ToString returns the string representation of the key
// It is in the following format:
// /collection/policy/[CollectionID]
func (k CollectionPolicyKey) ToString() string {
return fmt.Sprintf("%s/%s", COLLECTION_POLICY, fmt.Sprint(k.CollectionID))
}

func (k CollectionPolicyKey) Bytes() []byte {
return []byte(k.ToString())
}

func (k CollectionPolicyKey) ToDS() ds.Key {
return ds.NewKey(k.ToString())
}

// NewCollectionIndexKey creates a new CollectionIndexKey from a collection name and index name.
func NewCollectionIndexKey(colID immutable.Option[uint32], indexName string) CollectionIndexKey {
return CollectionIndexKey{CollectionID: colID, IndexName: indexName}
Expand Down
87 changes: 69 additions & 18 deletions db/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ func (db *db) createCollection(
}

col := db.newCollection(desc, schema)

for _, index := range desc.Indexes {
if _, err := col.createIndex(ctx, txn, index); err != nil {
return nil, err
Expand Down Expand Up @@ -591,6 +592,11 @@ func (db *db) getCollectionsByVersionID(
if err != nil {
return nil, err
}

err = collections[i].loadPolicy(ctx, txn)
if err != nil {
return nil, err
}
}

return collections, nil
Expand All @@ -608,11 +614,17 @@ func (db *db) getCollectionByID(ctx context.Context, txn datastore.Txn, id uint3
}

collection := db.newCollection(col, schema)

err = collection.loadIndexes(ctx, txn)
if err != nil {
return nil, err
}

err = collection.loadPolicy(ctx, txn)
if err != nil {
return nil, err
}

return collection, nil
}

Expand All @@ -633,11 +645,17 @@ func (db *db) getCollectionByName(ctx context.Context, txn datastore.Txn, name s
}

collection := db.newCollection(col, schema)

err = collection.loadIndexes(ctx, txn)
if err != nil {
return nil, err
}

err = collection.loadPolicy(ctx, txn)
if err != nil {
return nil, err
}

return collection, nil
}

Expand Down Expand Up @@ -670,6 +688,11 @@ func (db *db) getCollectionsBySchemaRoot(
if err != nil {
return nil, err
}

err = collection.loadPolicy(ctx, txn)
if err != nil {
return nil, err
}
}

return collections, nil
Expand All @@ -696,6 +719,11 @@ func (db *db) getAllCollections(ctx context.Context, txn datastore.Txn) ([]clien
if err != nil {
return nil, err
}

err = collection.loadPolicy(ctx, txn)
if err != nil {
return nil, err
}
}

return collections, nil
Expand All @@ -722,6 +750,11 @@ func (db *db) getAllActiveDefinitions(ctx context.Context, txn datastore.Txn) ([
return nil, err
}

err = collection.loadPolicy(ctx, txn)
if err != nil {
return nil, err
}

definitions[i] = collection.Definition()
}

Expand Down Expand Up @@ -865,24 +898,8 @@ func (c *collection) Create(ctx context.Context, doc *client.Document) error {
if err != nil {
return err
}
err = c.commitImplicitTxn(ctx, txn)
if err != nil {
return err
}

//fmt.Println("333333333333333333333333333333333333")
//spew.Dump(c.)
//fmt.Println("333333333333333333333333333333333333")

//c.db.ACPModule().RegisterDocCreation(
// ctx,
// policyID "123",
// collection string,
// creator string,
// docID string,
//)

return nil
return c.commitImplicitTxn(ctx, txn)
}

// CreateMany creates a collection of documents at once.
Expand Down Expand Up @@ -952,7 +969,41 @@ func (c *collection) create(ctx context.Context, txn datastore.Txn, doc *client.
return err
}

return c.indexNewDoc(ctx, txn, doc)
err = c.indexNewDoc(ctx, txn, doc)
if err != nil {
return err
}

// If this collection has access control enabled, and the acp module is not missing,
// then register this document with acp module and give the creator access to it.
// Note: if the request was not permissioned then we want to keep this document as
// public (can be accessed by all), even if this collection has a policy and resource.
// TODO-ACP: Add another condition to check if is a permissioned request (after signatures)
// Below should all be under signature identity block because:
// Total 8 cases, and 3 outputs
// if (permReq, permCol, acpMod) => Create with req signature
// if (permReq, permCol, !acpMod) => Error, no acp available
// if (permReq, !permCol, acpMod) => Normal no acp
// if (permReq, !permCol, !acpMod) => Normal no acp, so ignore missing acp, no error
// if (!permReq, permCol, acpMod) => Public doc, no register with acp, no signature
// if (!permReq, !permCol, acpMod) => Public doc, no register with acp, no signature
// if (!permReq, permCol, !acpMod) => Public doc, no register with acp, no signature, we ignore missing acp
// if (!permReq, !permCol, !acpMod) => Public doc, no register with acp, no signature, we ignore missing acp
if policyID, resourceName, hasPolicy := client.IsPermissioned(c); hasPolicy {
if !c.db.ACPModule().HasValue() {
return ErrCanNotCreateDocOnPermColNoACP
}

return c.db.ACPModule().Value().RegisterDocCreation(
ctx,
"cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", // TODO-ACP: Replace with signature identity
policyID,
resourceName,
doc.ID().String(),
)
}

return nil
}

// Update an existing document with the new values.
Expand Down
Loading

0 comments on commit 9e10f3d

Please sign in to comment.