| 421 | 421 | | }) |
| 422 | 422 | | .map(|membership| membership.slug.as_str()) |
| 423 | 423 | | .collect(); |
| 424 | | − | let granted: Vec<&str> = viewer |
| 424 | + | let mut granted: Vec<&str> = viewer |
| 425 | 425 | | .iter() |
| 426 | | − | .flat_map(|user| &user.grants) |
| 426 | + | .flat_map(|user| { |
| 427 | + | let token = user.token.as_deref(); |
| 428 | + | user.grants |
| 429 | + | .iter() |
| 430 | + | .filter(move |grant| token.is_none_or(|token| token.covers_repo(&grant.repo_id, &grant.workspace))) |
| 431 | + | }) |
| 427 | 432 | | .map(|grant| grant.repo_id.as_str()) |
| 428 | 433 | | .collect(); |
| 434 | + | // A fine-grained token's selected repositories, where its owner's |
| 435 | + | // membership reaches them. |
| 436 | + | if let Some(user) = viewer.as_ref() |
| 437 | + | && let Some(reach) = user.token.as_deref().and_then(|token| token.fine_grained.as_ref()) |
| 438 | + | && let Some(workspace) = reach.workspace.as_deref() |
| 439 | + | { |
| 440 | + | granted.extend( |
| 441 | + | reach |
| 442 | + | .repo_ids |
| 443 | + | .iter() |
| 444 | + | .filter(|id| access::granted(user, access::RepoRef { id, namespace: workspace, private: true }).is_some()) |
| 445 | + | .map(String::as_str), |
| 446 | + | ); |
| 447 | + | } |
| 429 | 448 | | let mut params: Vec<JsValue> = vec![ |
| 430 | 449 | | serde_json::to_string(&reading)?.into(), |
| 431 | 450 | | serde_json::to_string(&granted)?.into(), |