Skip to content
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

chore: improve access service #4689

Merged
merged 17 commits into from
Sep 19, 2023
Merged

chore: improve access service #4689

merged 17 commits into from
Sep 19, 2023

Conversation

gastonfournier
Copy link
Contributor

@gastonfournier gastonfournier commented Sep 13, 2023

About the changes

This enables us to use names instead of permission ids across all our APIs at the computational cost of searching for the ids in the DB but improving the API user experience

Open topics

We're using methods that are test-only and circumvent our business logic. This makes our test to rely on assumptions that are not always true because these assumptions are not validated frequently.

i.e. We are expecting that after removing a permission it's no longer there, but to test this, the permission has to be there before:

await accessService.removePermissionFromRole(
editRole.id,
permissions.CREATE_FEATURE,
'*',
);
expect(
await accessService.hasPermission(user, CREATE_FEATURE, 'some-project'),
).toBe(false);

But it seems that's not the case.

We'll look into improving this later.

@vercel
Copy link

vercel bot commented Sep 13, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Ignored Deployments
Name Status Preview Comments Updated (UTC)
unleash-docs ⬜️ Ignored (Inspect) Visit Preview Sep 19, 2023 8:40am
unleash-monorepo-frontend ⬜️ Ignored (Inspect) Visit Preview Sep 19, 2023 8:40am

nunogois
nunogois previously approved these changes Sep 13, 2023
Copy link
Member

@nunogois nunogois left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to me 🤷

chriswk
chriswk previously approved these changes Sep 13, 2023
Copy link
Member

@chriswk chriswk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable.

await accessService.addPermissionToRole(
role.id,
builtInRole.id,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test is adding a permission to the builtInRole, something that is not possible because of some validations at the service layer. Because the test is also creating a new role, I have the impression that the intention of the test is not to use the builtin role but the newly created one.

);
} else {
// this branch uses id permissions
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the reason why we're making this change, we want to be able to use names instead of ids for permissions

Comment on lines +6 to +7
id: joi.number(),
name: joi.string(),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation now expects that either you provide the id or the name (or both)

src/lib/db/access-store.ts Outdated Show resolved Hide resolved
src/lib/db/access-store.ts Outdated Show resolved Hide resolved
Comment on lines 90 to 92
environment: permissions.find(
(p) => p.name === (row.permission as string),
)?.environment,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the performance of this... a find inside a map is not ideal

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a problem? O(n*m) isn't an issue when n and m are small. Should be easy enough to build a Map earlier in the function and get O(1) lookups

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly what I had in mind

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume positive intentions here, I'm struggling with formulating the question.
Why are we fetching ids again? Wasn't the goal to change to names as the primary key?

Copy link
Contributor Author

@gastonfournier gastonfournier Sep 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wasn't the goal to change to names as the primary key?

Yes, but our OpenAPI spec states that ids are required while names are optional. Adding the name as required is a breaking change having to wait until the next major release. I think because of this reason we can't drop support for id's (customers might be using them already).

On another note, I think I solved the n*m problem with a map

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is correct, we can trade off some performance for correctness for a while. When we make a breaking change we can clean this up

@gastonfournier gastonfournier marked this pull request as ready for review September 15, 2023 10:24
@gastonfournier gastonfournier dismissed stale reviews from chriswk and nunogois September 15, 2023 10:29

A lot changed since this review

Comment on lines +21 to +25
// use for debugging only, try not to commit tests with verbose set to true
noLoggerProvider.setVerbose = (beVerbose: boolean) => {
verbose = beVerbose;
};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unrelated, but I thought it might be useful

Comment on lines -201 to -204
req.user = await userService.createUser({
email: '[email protected]',
rootRole: role.id,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've refactored this method, not sure what we were trying to do here:

  1. Get Viewer built-in role
  2. Create a user A
  3. Create a new role "token creator" without permissions
  4. Add CREATE_PROJECT permission to the Viewer role (shouldn't be possible to add permissions programmatically)
  5. Add user A to the "token creator role" in project 'default' (now A has the root role Viewer and "token creator role" in 'default')
  6. Create a new user B with root role Viewer (that now has the new permission)
  7. Associate req.user to the newly created user B

Then the test tries to create an API token using the newly created user B.

Why did we take all those efforts to setup user A if we're not using it in the test? Apparently the only important steps are the ones in bold. At least taking into account the title of the test: A role with only CREATE_PROJECT_API_TOKEN can create project tokens

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guessing copy paste bug. Something something don't trust tests you haven't seen fail

email: '[email protected]',
rootRole: role.id,
});
req.user = user;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On this side:

  1. Get viewer role
  2. Create a user with Viewer role as root role
  3. Create a new role with CREATE_PROJECT_API_TOKEN permission
  4. Add the user to the role created in default project
  5. Associate req.user to the user created

Later we validate if this user can create an API token in the project where they have the role with that specific permission

src/lib/db/access-store.ts Show resolved Hide resolved
src/lib/db/access-store.test.ts Outdated Show resolved Hide resolved
src/lib/db/access-store.test.ts Outdated Show resolved Hide resolved
async (permissions) => {
const access = db.stores.accessStore as AccessStore;
const result = await access.resolvePermissions(permissions);
expect(result).toStrictEqual(permissions);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to say: no query to the DB here

Comment on lines +93 to +96
const rows = await this.db
.select('id', 'permission')
.from(T.PERMISSIONS)
.whereIn('permission', permissionNames);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same query as here: https://github.com/Unleash/unleash/pull/4689/files#diff-5cb4eca71e6a03ec3d123986d657368a3386f80e986dfef2e3feb0abb0673d87L706-L709 but now it can be used in all scenarios of adding permissions to a role

Copy link
Member

@sighphyre sighphyre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, I think with the tests and timers this is good enough to try

@gastonfournier gastonfournier merged commit 2186e2b into main Sep 19, 2023
7 checks passed
@gastonfournier gastonfournier deleted the improve-access-service branch September 19, 2023 09:36
gastonfournier added a commit that referenced this pull request Sep 19, 2023
gastonfournier added a commit that referenced this pull request Sep 19, 2023
Reverts #4689 temporarily to figure out what's the
problem with the failing test
gastonfournier added a commit that referenced this pull request Sep 19, 2023
## About the changes
In #4689 I forgot to add backward
compatibility for a public method that was being used in Enterprise.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants