-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CSS-5674 Replace database-stored access with OpenFGA #1051
CSS-5674 Replace database-stored access with OpenFGA #1051
Conversation
- removes the need for basic auth from JIMM as it will now use JWTs which Juju controllers trust
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
internal/jimm/cloud.go
Outdated
uca = a | ||
break | ||
currentRelation := targetOfgaUser.GetCloudAccess(ctx, ct) | ||
if currentRelation != ofganames.NoRelation { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might this be better as a switch statement?
internal/jimm/cloud.go
Outdated
} | ||
return nil | ||
}) | ||
if err := api.GrantCloudAccess(ctx, ct, ut, access); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to do this? This is assigning access on the Juju side I believe, but Juju won't be checking it's own access permissions, it will just rely on whatever is in the JWT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah looking at
Line 488 in 5e85687
// TODO (alesstimec) This will no longer be needed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agreed.. this should no longer be needed..
internal/jimm/model.go
Outdated
} | ||
} | ||
|
||
if err := api.GrantModelAccess(ctx, mt, ut, access); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, I don't think we need these calls anymore.
internal/openfga/user.go
Outdated
existingRelations[tt.Tuple.Relation] = nil | ||
} | ||
|
||
if ct == lastCT { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this come before we range over tts
?
|
||
lastCT := "" | ||
existingRelations := map[Relation]interface{}{} | ||
for { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also what do you think about in a future PR if we create an abstraction over this to have a cleaner way of getting all the results?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I checked this is the only place we're iterating using continuation tokens. So, at the moment, there's no need to split this part away. But you're right. If we're going to do the pagination again, we should do it in a reusable way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some questions and potential changes
internal/jimm/cloud.go
Outdated
return errors.E(op, errors.CodeBadRequest, "failed to recognize given access", err) | ||
} | ||
|
||
err = j.doCloudAdmin(ctx, user, ct, func(c *dbmodel.Cloud, api API) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The callback still takes dbmodel.Cloud, is this needed now? I'd presume not right?
internal/jimm/cloud.go
Outdated
if err != nil { | ||
ale.Params["err"] = err.Error() | ||
return errors.E(op, err) | ||
return errors.E(err, "cannot update OpenFGA record after updating cloud") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't the error more a tuple issue just from ofga?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also, please include op in the error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since there's no Juju API call anymore, I changed the error message to failed to set cloud access
.
internal/jimm/cloud.go
Outdated
} | ||
return nil | ||
}) | ||
if err := api.RevokeCloudAccess(ctx, ct, ut, access); err != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't think this is needed either, api typically hits the controller/model, we wanna keep this purely in JIMM
internal/jimm/cloud.go
Outdated
return err | ||
} | ||
|
||
err = targetOfgaUser.SetCloudAccess(ctx, ct, targetRelation) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sometime we do err := p(); err != nil, and sometimes two lines, let's try keep it inline if's with errs where we can
internal/jimm/model.go
Outdated
requiredAccess = "read" | ||
} | ||
|
||
err = j.doModel(ctx, user, mt, requiredAccess, func(m *dbmodel.Model, api API) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like doCloud, doCloudAdmin, doModelAdmin, we don't need the model passing in anymore do we?
internal/jimm/model.go
Outdated
return err | ||
} | ||
|
||
err = targetOfgaUser.UnsetModelAccess(ctx, mt, relationsToRevoke...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We aren't transactional ATM are we? So this may remove some but not all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, at last we decided to go with the transactional approach. To be precise, if we're removing a few tuples, all-or-none will be deleted.
@@ -318,6 +339,56 @@ func setResourceAccess[T ofganames.ResourceTagger](ctx context.Context, user *Us | |||
return nil | |||
} | |||
|
|||
// unsetMultipleResourceAccesses deletes relations that correspond to the requested resource access, atomically. | |||
// Note that the action is idempotent (i.e., does not return error if any of the relations does not exist). | |||
func unsetMultipleResourceAccesses[T ofganames.ResourceTagger](ctx context.Context, user *User, resource T, relations []Relation) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Honestly I'm a bit lost with ct and tts, tts is targetTuples I'm presuming? and CT is convertTag? Would prefer something like lastCT for lastConvertTag and just be explicit so it's easier on the brain initially
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. CT stands for continuation token and TT(s) is timestamped-tuple(s). But you're right. I've improved the namings.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think honestly just call it "lastContinuationToken", as I'll probably forget in another 3 months
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've already changed it to just token
and lastToken
. But can change it to continuationToken
and lastContinuationToken
if you think so.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it's not the "go" way, but we have a lot of context and I feel personally I'll just forget what last token is :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will sort it now.
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, but please add logging so that we have an easier time debugging issues in the future..
internal/jimm/cloud.go
Outdated
}, | ||
targetRelation, err := ToCloudRelation(access) | ||
if err != nil { | ||
return errors.E(op, errors.CodeBadRequest, "failed to recognize given access", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
could we, please log the access
? might be useful for debugging purposes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think it should be a debug
entry? or error
? If it's for debugging I think debug
should suffice.
}) | ||
|
||
if err != nil { | ||
return errors.E(op, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please log this error
}) | ||
|
||
if err != nil { | ||
return errors.E(op, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please log this error
}) | ||
|
||
if err != nil { | ||
return errors.E(op, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please the log this error
}) | ||
|
||
if err != nil { | ||
return errors.E(op, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please log this error
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm bar ales' request for log lines
Signed-off-by: Babak K. Shandiz <babak.k.shandiz@canonical.com>
I've already added them. |
Description
This PR replaces remainings of JIMM.v1 access storage with OpenFGA; namely the following has been updated:
I've added a lot of test cases. To make the review easier here is the list tests:
GrantCloudAccess
RevokeCloudAccess
GrantModelAccess
RevokeModelAccess
Fixes CSS-5674
Change of behavior:
ModifyCloudAccess
Note that there is a change in behavior for when revoking a users's access to a cloud. In the old implementation, if we revoked the
admin
access, the user would be granted with theadd-model
access:But in the new implementation, we just drop the
admin
relation and do not create a separate tuple to grantadd-model
access.Change of behavior:
ModifyModelAccess
In the old implementation when we're revoking a user's model access, we'd do it like this (i.e., demoting user access by one level):
But in the new implementation, we don't add new relations to lower accesses. We just drop the relation together with all higher accesses. For example, when revoking
read
access, we dropread
,write
andadmin
all together.Engineering checklist
Check only items that apply