Skip to content

Commit a1b10d0

Browse files
committed
Merge branch 'main' into fix-attach
2 parents 11fea7c + c752682 commit a1b10d0

File tree

15 files changed

+432
-158
lines changed

15 files changed

+432
-158
lines changed

common/src/api/external/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -726,7 +726,7 @@ pub struct Instance {
726726

727727
// DISKS
728728

729-
/// Client view of an [`Disk`]
729+
/// Client view of a [`Disk`]
730730
#[derive(ObjectIdentity, Clone, Debug, Deserialize, Serialize, JsonSchema)]
731731
pub struct Disk {
732732
#[serde(flatten)]

nexus/src/authz/api_resources.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,7 @@ impl ApiResourceWithRolesType for Organization {
391391
pub enum OrganizationRoles {
392392
Admin,
393393
Collaborator,
394+
Viewer,
394395
}
395396

396397
impl db::model::DatabaseString for OrganizationRoles {
@@ -400,13 +401,15 @@ impl db::model::DatabaseString for OrganizationRoles {
400401
match self {
401402
OrganizationRoles::Admin => "admin",
402403
OrganizationRoles::Collaborator => "collaborator",
404+
OrganizationRoles::Viewer => "viewer",
403405
}
404406
}
405407

406408
fn from_database_string(s: &str) -> Result<Self, Self::Error> {
407409
match s {
408410
"admin" => Ok(OrganizationRoles::Admin),
409411
"collaborator" => Ok(OrganizationRoles::Collaborator),
412+
"viewer" => Ok(OrganizationRoles::Viewer),
410413
_ => Err(anyhow!(
411414
"unsupported Organization role from database: {:?}",
412415
s

nexus/src/authz/omicron.polar

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ has_role(actor: AuthenticatedActor, role: String, resource: Resource)
6767
# - silo.viewer (can read most resources within the Silo)
6868
# - organization.admin (complete control over an organization)
6969
# - organization.collaborator (can manage Projects)
70+
# - organization.viewer (can read most resources within the Organization)
7071
# - project.admin (complete control over a Project)
7172
# - project.collaborator (can manage all resources within the Project)
7273
# - project.viewer (can read most resources within the Project)
@@ -164,22 +165,22 @@ resource Organization {
164165
"read",
165166
"create_child",
166167
];
167-
roles = [ "admin", "collaborator" ];
168+
roles = [ "admin", "collaborator", "viewer" ];
168169

169170
# Roles implied by other roles on this resource
171+
"viewer" if "collaborator";
170172
"collaborator" if "admin";
171173

172174
# Permissions granted directly by roles on this resource
173-
"list_children" if "collaborator";
174-
"read" if "collaborator";
175+
"list_children" if "viewer";
176+
"read" if "viewer";
175177
"create_child" if "collaborator";
176178
"modify" if "admin";
177179

178180
# Roles implied by roles on this resource's parent (Silo)
179181
relations = { parent_silo: Silo };
180182
"admin" if "collaborator" on "parent_silo";
181-
"read" if "viewer" on "parent_silo";
182-
"list_children" if "viewer" on "parent_silo";
183+
"viewer" if "viewer" on "parent_silo";
183184
}
184185
has_relation(silo: Silo, "parent_silo", organization: Organization)
185186
if organization.silo = silo;
@@ -206,7 +207,7 @@ resource Project {
206207
# Roles implied by roles on this resource's parent (Organization)
207208
relations = { parent_organization: Organization };
208209
"admin" if "collaborator" on "parent_organization";
209-
"viewer" if "list_children" on "parent_organization";
210+
"viewer" if "viewer" on "parent_organization";
210211
}
211212
has_relation(organization: Organization, "parent_organization", project: Project)
212213
if project.organization = organization;
@@ -253,7 +254,7 @@ has_relation(user: SiloUser, "silo_user", ssh_key: SshKey)
253254
# of the API path (e.g., "/images") or as an implementation detail of the system
254255
# (in the case of console sessions and "Database"). The policies are
255256
# either statically-defined in this file or driven by role assignments on the
256-
# Fleet.
257+
# Fleet. None of these resources defines their own roles.
257258
#
258259

259260
# Describes the policy for accessing "/images" (in the API)
@@ -320,25 +321,11 @@ resource Database {
320321
# other general functions.
321322
"modify"
322323
];
323-
roles = [
324-
# All authenticated users get the "user" role, which grants the
325-
# "query" permission. See above.
326-
"user",
327-
328-
# The special "db-init" user gets the "init" role, which grants the
329-
# additional "modify" permission.
330-
"init"
331-
];
332-
333-
# See above.
334-
"query" if "user";
335-
336-
"user" if "init";
337-
"modify" if "init";
338324
}
339325

340-
# All authenticated users have the "user" role on the database.
341-
has_role(_actor: AuthenticatedActor, "user", _resource: Database);
326+
# All authenticated users have the "query" permission on the database.
327+
has_permission(_actor: AuthenticatedActor, "query", _resource: Database);
328+
342329
# The "db-init" user is the only one with the "init" role.
343-
has_role(actor: AuthenticatedActor, "init", _resource: Database)
330+
has_permission(actor: AuthenticatedActor, "modify", _resource: Database)
344331
if actor = USER_DB_INIT;

nexus/src/db/fixed_data/role_builtin.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use lazy_static::lazy_static;
77
use omicron_common::api;
88

9-
#[derive(Clone)]
9+
#[derive(Clone, Debug)]
1010
pub struct RoleBuiltinConfig {
1111
pub resource_type: api::external::ResourceType,
1212
pub role_name: &'static str,
@@ -67,6 +67,11 @@ lazy_static! {
6767
},
6868
ORGANIZATION_ADMINISTRATOR.clone(),
6969
ORGANIZATION_COLLABORATOR.clone(),
70+
RoleBuiltinConfig {
71+
resource_type: api::external::ResourceType::Organization,
72+
role_name: "viewer",
73+
description: "Organization Viewer",
74+
},
7075
RoleBuiltinConfig {
7176
resource_type: api::external::ResourceType::Project,
7277
role_name: "admin",
@@ -84,3 +89,52 @@ lazy_static! {
8489
},
8590
];
8691
}
92+
93+
#[cfg(test)]
94+
mod test {
95+
use super::BUILTIN_ROLES;
96+
use crate::authz;
97+
use crate::db::model::DatabaseString;
98+
use omicron_common::api::external::ResourceType;
99+
use strum::IntoEnumIterator;
100+
101+
#[test]
102+
fn test_fixed_role_data() {
103+
// Every role that's defined in the public API as assignable on a
104+
// resource must have a corresponding entry in BUILTIN_ROLES above.
105+
// The reverse is not necessarily true because we have some internal
106+
// roles that are not exposed to end users.
107+
check_public_roles::<authz::FleetRoles>(ResourceType::Fleet);
108+
check_public_roles::<authz::SiloRoles>(ResourceType::Silo);
109+
check_public_roles::<authz::OrganizationRoles>(
110+
ResourceType::Organization,
111+
);
112+
check_public_roles::<authz::ProjectRoles>(ResourceType::Project);
113+
}
114+
115+
fn check_public_roles<T>(resource_type: ResourceType)
116+
where
117+
T: std::fmt::Debug + DatabaseString + IntoEnumIterator,
118+
{
119+
for variant in T::iter() {
120+
let role_name = variant.to_database_string();
121+
122+
let found = BUILTIN_ROLES.iter().find(|role_config| {
123+
role_config.resource_type == resource_type
124+
&& role_config.role_name == role_name
125+
});
126+
if let Some(found_config) = found {
127+
println!(
128+
"variant: {:?} found fixed data {:?}",
129+
variant, found_config
130+
);
131+
} else {
132+
panic!(
133+
"found public role {:?} on {:?} with no corresponding \
134+
built-in role",
135+
role_name, resource_type
136+
);
137+
}
138+
}
139+
}
140+
}

nexus/tests/integration_tests/endpoints.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//! This is used for various authz-related tests.
88
//! THERE ARE NO TESTS IN THIS FILE.
99
10+
use crate::integration_tests::unauthorized::HTTP_SERVER;
1011
use http::method::Method;
1112
use lazy_static::lazy_static;
1213
use nexus_test_utils::RACK_UUID;
@@ -234,7 +235,7 @@ lazy_static! {
234235
name: DEMO_IMAGE_NAME.clone(),
235236
description: String::from(""),
236237
},
237-
source: params::ImageSource::Url(String::from("http://127.0.0.1:5555/image.raw")),
238+
source: params::ImageSource::Url(HTTP_SERVER.url("/image.raw").to_string()),
238239
block_size: params::BlockSize::try_from(4096).unwrap(),
239240
};
240241

nexus/tests/integration_tests/roles_builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ async fn test_roles_builtin(cptestctx: &ControlPlaneTestContext) {
3434
("fleet.viewer", "Fleet Viewer"),
3535
("organization.admin", "Organization Administrator"),
3636
("organization.collaborator", "Organization Collaborator"),
37+
("organization.viewer", "Organization Viewer"),
3738
("project.admin", "Project Administrator"),
3839
("project.collaborator", "Project Collaborator"),
3940
("project.viewer", "Project Viewer"),

nexus/tests/integration_tests/unauthorized.rs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,24 +55,6 @@ async fn test_unauthorized(cptestctx: &ControlPlaneTestContext) {
5555
let client = &cptestctx.external_client;
5656
let log = &cptestctx.logctx.log;
5757

58-
// Run a httptest server
59-
let server = ServerBuilder::new()
60-
.bind_addr("127.0.0.1:5555".parse().unwrap())
61-
.run()
62-
.unwrap();
63-
64-
// Fake some data
65-
server.expect(
66-
Expectation::matching(request::method_path("HEAD", "/image.raw"))
67-
.times(1..)
68-
.respond_with(
69-
status_code(200).append_header(
70-
"Content-Length",
71-
format!("{}", 4096 * 1000),
72-
),
73-
),
74-
);
75-
7658
// Create test data.
7759
info!(log, "setting up resource hierarchy");
7860
for request in &*SETUP_REQUESTS {
@@ -118,7 +100,7 @@ EXAMPLE: 0 3111 5555 3111 5555 5555 0 /organizations
118100
The number in each cell is the last digit of the 400-level response
119101
that was expected for this test case.
120102
121-
In this case, an unauthenthicated request to "GET /organizations" returned
103+
In this case, an unauthenticated request to "GET /organizations" returned
122104
401. All requests to "PUT /organizations" returned 405.
123105
124106
G GET PUT POST DEL TRCE G URL
@@ -141,6 +123,25 @@ struct SetupReq {
141123
}
142124

143125
lazy_static! {
126+
pub static ref HTTP_SERVER: httptest::Server = {
127+
// Run a httptest server
128+
let server = ServerBuilder::new().run().unwrap();
129+
130+
// Fake some data
131+
server.expect(
132+
Expectation::matching(request::method_path("HEAD", "/image.raw"))
133+
.times(1..)
134+
.respond_with(
135+
status_code(200).append_header(
136+
"Content-Length",
137+
format!("{}", 4096 * 1000),
138+
),
139+
),
140+
);
141+
142+
server
143+
};
144+
144145
/// List of requests to execute at setup time
145146
static ref SETUP_REQUESTS: Vec<SetupReq> = vec![
146147
// Create a separate Silo (not used for anything else)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
variant Admin: serialized form = admin
22
variant Collaborator: serialized form = collaborator
3+
variant Viewer: serialized form = viewer

openapi/nexus.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5304,7 +5304,7 @@
53045304
]
53055305
},
53065306
"Disk": {
5307-
"description": "Client view of an [`Disk`]",
5307+
"description": "Client view of a [`Disk`]",
53085308
"type": "object",
53095309
"properties": {
53105310
"block_size": {
@@ -6643,7 +6643,8 @@
66436643
"type": "string",
66446644
"enum": [
66456645
"admin",
6646-
"collaborator"
6646+
"collaborator",
6647+
"viewer"
66476648
]
66486649
},
66496650
"OrganizationRolesPolicy": {

sled-agent/src/bootstrap/agent.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
use super::config::{Config, BOOTSTRAP_AGENT_PORT};
88
use super::discovery;
99
use super::params::SledAgentRequest;
10+
use super::rss_handle::RssHandle;
1011
use super::trust_quorum::{
1112
self, RackSecret, ShareDistribution, TrustQuorumError,
1213
};
1314
use super::views::{ShareResponse, SledAgentResponse};
1415
use crate::config::Config as SledConfig;
1516
use crate::illumos::dladm::{self, Dladm, PhysicalLink};
1617
use crate::illumos::zone::Zones;
17-
use crate::rack_setup::service::Service as RackSetupService;
1818
use crate::server::Server as SledServer;
1919
use omicron_common::address::get_sled_address;
2020
use omicron_common::api::external::{Error as ExternalError, MacAddr};
@@ -90,7 +90,7 @@ pub(crate) struct Agent {
9090
peer_monitor: discovery::PeerMonitor,
9191
share: Option<ShareDistribution>,
9292

93-
rss: Mutex<Option<RackSetupService>>,
93+
rss: Mutex<Option<RssHandle>>,
9494
sled_agent: Mutex<Option<SledServer>>,
9595
sled_config: SledConfig,
9696
}
@@ -381,8 +381,8 @@ impl Agent {
381381
// Initializes the Rack Setup Service.
382382
async fn start_rss(&self, config: &Config) -> Result<(), BootstrapError> {
383383
if let Some(rss_config) = &config.rss_config {
384-
let rss = RackSetupService::new(
385-
self.parent_log.new(o!("component" => "RSS")),
384+
let rss = RssHandle::start_rss(
385+
&self.parent_log,
386386
rss_config.clone(),
387387
self.peer_monitor.observer().await,
388388
);

sled-agent/src/bootstrap/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ pub mod discovery;
1111
mod http_entrypoints;
1212
pub mod multicast;
1313
pub(crate) mod params;
14+
pub(crate) mod rss_handle;
1415
pub mod server;
1516
mod spdm;
1617
pub mod trust_quorum;

0 commit comments

Comments
 (0)