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

refactor(permissions): split up Descriptor into Allow, Deny, and Query #25508

Merged
merged 41 commits into from
Sep 16, 2024

Conversation

dsherret
Copy link
Member

@dsherret dsherret commented Sep 8, 2024

This makes the permission system more versatile.

@lowlighter
Copy link
Contributor

Any chance taking a look at #14668 during this refactor ?

@dsherret
Copy link
Member Author

dsherret commented Sep 8, 2024

@lowlighter no, that would be too many changes for this PR.

cli/file_fetcher.rs Outdated Show resolved Hide resolved
cli/lsp/registries.rs Outdated Show resolved Hide resolved
Comment on lines +145 to +147
// todo(dsherret): don't have this function use the properties of
// permission container
let permissions_container = state.borrow_mut::<PermissionsContainer>();
Copy link
Member

Choose a reason for hiding this comment

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

What's wrong with that?

Copy link
Member Author

@dsherret dsherret Sep 9, 2024

Choose a reason for hiding this comment

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

It's exposing a mutex, it's having code that shouldn't be done/exposed at this level (parsing descriptors), and it's coupling what could be its private api to this part of the code.

Copy link
Member

Choose a reason for hiding this comment

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

Open an issue to keep track of it? Or are you planning to address it before landing?

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

I want to give runtime/permissions/lib.rs one more pass tomorrow

Comment on lines 311 to 313
pub trait QueryDescriptor: Debug {
type AllowDesc: AllowDescriptor<Query = Self>;
type DenyDesc: DenyDescriptor<Query = Self>;
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit confusing - effectively there are circular dependencies between these three types. Do you see any chance we could decouple that with a helper type?

Copy link
Member

Choose a reason for hiding this comment

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

Also could you add some docstrings around these traits?

Copy link
Member Author

Choose a reason for hiding this comment

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

I got rid of the additional traits and now there's only a QueryDescriptor.

fn is_granted(&self, desc: Option<&TQuery>) -> bool {
match desc {
Some(desc) => {
self.granted_global || self.granted_list.iter().any(|v| v.matches(desc))
Copy link
Member

Choose a reason for hiding this comment

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

Why are we using .matches() instead of .stronger_than() now?

Copy link
Member Author

Choose a reason for hiding this comment

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

There are multiple specific methods. I've grouped them all on query now.

Comment on lines 1077 to 1081
/// This variant can't be used to grant permissions. It's mostly
/// used so that prompts and everything works the same way as when
/// the command is resolved, meaning that a script can't tell
/// if a command is resolved or not based on how long something
/// takes to ask for permissions.
Copy link
Member

Choose a reason for hiding this comment

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

Could you reword this docstring? I'm having trouble understanding it - what does "This variant can't be used to grant permissions" mean?

Copy link
Member Author

Choose a reason for hiding this comment

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

I updated it a bit. The variant gets created when the command path can't be resolved, so it will prompt the users for permissions about it, but it doesn't actually do anything when the user grants permissions. It's to prevent someone trying to probe for permissions to find out what's on the path.

Comment on lines +2022 to +2026
// todo(dsherret): make both of these private as the functionality
// can just be methods on PermissionsContainer. Additionally, a separate
// struct should be created in here that handles creating child permissions
// so that the code is not so verbose elsewhere.
pub descriptor_parser: Arc<dyn PermissionDescriptorParser>,
Copy link
Member

Choose a reason for hiding this comment

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

Can you open an issue about this one?

Copy link
Member Author

@dsherret dsherret Sep 15, 2024

Choose a reason for hiding this comment

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

I put it in #25634

runtime/permissions/lib.rs Outdated Show resolved Hide resolved
${JSON.stringify(Object.keys(expectedEnv))}.map(k => Deno.env.get(k))
${
JSON.stringify(Object.keys(expectedEnv))
}.map(k => Deno.env.get(k) ?? null)
Copy link
Member

Choose a reason for hiding this comment

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

why is this change required? I assume it's unrelated?

Copy link
Member Author

Choose a reason for hiding this comment

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

undefined isn't valid JSON, but null is (it does a JSON.parse below). I ran into this issue while developing where I introduced a bug, then it was undefined, then the error message was not great.

Copy link
Member

@lucacasonato lucacasonato left a comment

Choose a reason for hiding this comment

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

Did you benchmark these changes?

From what I can tell, we do a lot more canonicalization for FS ops now (maybe including more getcwd calls?) - I wonder if sync FS ops will slow down due to this. From my reading of the code, this difference would only be visible in non --allow-all contexts.

runtime/permissions.rs Outdated Show resolved Hide resolved
) -> Result<(), AnyError> {
skip_check_if_is_permission_fully_granted!(self);
let (result, prompted, is_allow_all) = self
Copy link
Member

Choose a reason for hiding this comment

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

In a follow up we should change the return value here to an enum or struct. This is unreadable.

runtime/permissions/lib.rs Outdated Show resolved Hide resolved
runtime/permissions/lib.rs Outdated Show resolved Hide resolved
runtime/permissions/lib.rs Outdated Show resolved Hide resolved
@dsherret
Copy link
Member Author

From what I can tell, we do a lot more canonicalization for FS ops now (maybe including more getcwd calls?)

It should be doing the same or less now with --allow-all. It doesn't canonicalize like before.

@lucacasonato
Copy link
Member

I meant without --allow-all

@dsherret
Copy link
Member Author

dsherret commented Sep 16, 2024

Yeah should be the same without allow-run. It was doing the cwd call before as well.

@dsherret
Copy link
Member Author

dsherret commented Sep 16, 2024

Some benchmarks:

$ cat main.ts
for (var i = 0; i < 100_000; i++) {
  const text = Deno.readTextFileSync("./main.ts");
  console.log(text.length);
}
$ hyperfine --warmup=3 "./deno_main run --allow-read=main.ts main.ts" "./deno_new run --allow-read=main.ts main.ts"
Benchmark 1: ./deno_main run --allow-read=main.ts main.ts
  Time (mean ± σ):      1.955 s ±  0.018 s    [User: 0.513 s, System: 1.446 s]
  Range (min … max):    1.931 s …  1.989 s    10 runs
 
Benchmark 2: ./deno_new run --allow-read=main.ts main.ts
  Time (mean ± σ):      1.932 s ±  0.011 s    [User: 0.495 s, System: 1.441 s]
  Range (min … max):    1.920 s …  1.953 s    10 runs
 
Summary
  ./deno_new run --allow-read=main.ts main.ts ran
    1.01 ± 0.01 times faster than ./deno_main run --allow-read=main.ts main.ts
$ hyperfine --warmup=3 "./deno_main run --allow-all main.ts" "./deno_new run --allow-all main.ts"
Benchmark 1: ./deno_main run --allow-all main.ts
  Time (mean ± σ):      1.907 s ±  0.015 s    [User: 0.468 s, System: 1.442 s]
  Range (min … max):    1.882 s …  1.933 s    10 runs
 
Benchmark 2: ./deno_new run --allow-all main.ts
  Time (mean ± σ):      1.893 s ±  0.014 s    [User: 0.462 s, System: 1.435 s]
  Range (min … max):    1.876 s …  1.914 s    10 runs
 
Summary
  ./deno_new run --allow-all main.ts ran
    1.01 ± 0.01 times faster than ./deno_main run --allow-all main.ts

Copy link
Member

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

LGTM

@dsherret dsherret merged commit 62e9525 into denoland:main Sep 16, 2024
17 checks passed
@dsherret dsherret deleted the refactor_permissions branch September 16, 2024 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants