Skip to content

Commit d70fbbe

Browse files
authored
minor: refactor session create methods (#7827)
There should be no functional changes here, though the internal error messages are now slightly different between saml login and local login, where before they were the same. Ran into this while working #7339. This logic (which I wrote originally and shuffled around in #7374) never really made sense, and I figured it's good prep for #7818 and friends to clean it up. The core of the change here is updating two existing functions that returned `Result<Option<User>, Error>` to just return `Result<User, Error>` and move the logic about what we do when the user was `None` inside each function. In both cases, when the user was `None` we ended up with an `Error::Unauthenticated` anyway, so we can just do that a moment earlier and eliminate a lot of misdirection.
1 parent 74a2454 commit d70fbbe

File tree

5 files changed

+47
-98
lines changed

5 files changed

+47
-98
lines changed

nexus/src/app/login.rs

+1-20
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@ use dropshot::{HttpError, HttpResponseFound, http_response_found};
66
use nexus_auth::context::OpContext;
77
use nexus_db_model::{ConsoleSession, Name};
88
use nexus_db_queries::authn::silos::IdentityProviderType;
9-
use nexus_db_queries::db::identity::Asset;
109
use nexus_types::external_api::{params::RelativeUri, shared::RelayState};
11-
use omicron_common::api::external::Error;
1210

1311
impl super::Nexus {
1412
pub(crate) async fn login_saml_redirect(
@@ -77,28 +75,11 @@ impl super::Nexus {
7775
&authenticated_subject,
7876
)
7977
.await?;
80-
let session = self.create_session(opctx, user).await?;
78+
let session = self.session_create(opctx, &user).await?;
8179
let next_url = relay_state
8280
.and_then(|r| r.redirect_uri)
8381
.map(|u| u.to_string())
8482
.unwrap_or_else(|| "/".to_string());
8583
Ok((session, next_url))
8684
}
87-
88-
// TODO: move this logic, it's weird
89-
pub(crate) async fn create_session(
90-
&self,
91-
opctx: &OpContext,
92-
user: Option<nexus_db_queries::db::model::SiloUser>,
93-
) -> Result<ConsoleSession, Error> {
94-
let session = match user {
95-
Some(user) => self.session_create(&opctx, user.id()).await?,
96-
None => Err(Error::Unauthenticated {
97-
internal_message: String::from(
98-
"no matching user found or credentials were not valid",
99-
),
100-
})?,
101-
};
102-
Ok(session)
103-
}
10485
}

nexus/src/app/session.rs

+3-29
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use nexus_db_queries::authn::Reason;
1010
use nexus_db_queries::authz;
1111
use nexus_db_queries::context::OpContext;
1212
use nexus_db_queries::db;
13+
use nexus_db_queries::db::identity::Asset;
1314
use nexus_db_queries::db::lookup::LookupPath;
1415
use omicron_common::api::external::CreateResult;
1516
use omicron_common::api::external::DeleteResult;
@@ -34,40 +35,13 @@ fn generate_session_token() -> String {
3435
}
3536

3637
impl super::Nexus {
37-
async fn login_allowed(
38-
&self,
39-
opctx: &OpContext,
40-
silo_user_id: Uuid,
41-
) -> Result<bool, Error> {
42-
// Was this silo user deleted?
43-
let fetch_result = LookupPath::new(opctx, &self.db_datastore)
44-
.silo_user_id(silo_user_id)
45-
.fetch()
46-
.await;
47-
48-
match fetch_result {
49-
// if the silo user was deleted, they're not allowed to log in :)
50-
Err(Error::ObjectNotFound { .. }) => Ok(false),
51-
Err(e) => Err(e),
52-
// they're allowed
53-
Ok(_) => Ok(true),
54-
}
55-
}
56-
5738
pub(crate) async fn session_create(
5839
&self,
5940
opctx: &OpContext,
60-
user_id: Uuid,
41+
user: &db::model::SiloUser,
6142
) -> CreateResult<db::model::ConsoleSession> {
62-
if !self.login_allowed(opctx, user_id).await? {
63-
return Err(Error::Unauthenticated {
64-
internal_message: "User not allowed to login".to_string(),
65-
});
66-
}
67-
6843
let session =
69-
db::model::ConsoleSession::new(generate_session_token(), user_id);
70-
44+
db::model::ConsoleSession::new(generate_session_token(), user.id());
7145
self.db_datastore.session_create(opctx, session).await
7246
}
7347

nexus/src/app/silo.rs

+36-31
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ impl super::Nexus {
369369
authz_silo: &authz::Silo,
370370
db_silo: &db::model::Silo,
371371
authenticated_subject: &authn::silos::AuthenticatedSubject,
372-
) -> LookupResult<Option<db::model::SiloUser>> {
372+
) -> LookupResult<db::model::SiloUser> {
373373
// XXX create user permission?
374374
opctx.authorize(authz::Action::CreateChild, authz_silo).await?;
375375
opctx.authorize(authz::Action::ListChildren, authz_silo).await?;
@@ -383,35 +383,38 @@ impl super::Nexus {
383383
)
384384
.await?;
385385

386-
let (authz_silo_user, db_silo_user) =
387-
if let Some(existing_silo_user) = fetch_result {
388-
existing_silo_user
389-
} else {
390-
// In this branch, no user exists for the authenticated subject
391-
// external id. The next action depends on the silo's user
392-
// provision type.
393-
match db_silo.user_provision_type {
394-
// If the user provision type is ApiOnly, do not create a
395-
// new user if one does not exist.
396-
db::model::UserProvisionType::ApiOnly => {
397-
return Ok(None);
398-
}
386+
let (authz_silo_user, db_silo_user) = if let Some(existing_silo_user) =
387+
fetch_result
388+
{
389+
existing_silo_user
390+
} else {
391+
// In this branch, no user exists for the authenticated subject
392+
// external id. The next action depends on the silo's user
393+
// provision type.
394+
match db_silo.user_provision_type {
395+
// If the user provision type is ApiOnly, do not create a
396+
// new user if one does not exist.
397+
db::model::UserProvisionType::ApiOnly => {
398+
return Err(Error::Unauthenticated {
399+
internal_message: "User must exist before login when user provision type is ApiOnly".to_string(),
400+
});
401+
}
399402

400-
// If the user provision type is JIT, then create the user if
401-
// one does not exist
402-
db::model::UserProvisionType::Jit => {
403-
let silo_user = db::model::SiloUser::new(
404-
authz_silo.id(),
405-
Uuid::new_v4(),
406-
authenticated_subject.external_id.clone(),
407-
);
403+
// If the user provision type is JIT, then create the user if
404+
// one does not exist
405+
db::model::UserProvisionType::Jit => {
406+
let silo_user = db::model::SiloUser::new(
407+
authz_silo.id(),
408+
Uuid::new_v4(),
409+
authenticated_subject.external_id.clone(),
410+
);
408411

409-
self.db_datastore
410-
.silo_user_create(&authz_silo, silo_user)
411-
.await?
412-
}
412+
self.db_datastore
413+
.silo_user_create(&authz_silo, silo_user)
414+
.await?
413415
}
414-
};
416+
}
417+
};
415418

416419
// Gather a list of groups that the user is part of based on what the
417420
// IdP sent us. Also, if the silo user provision type is Jit, create
@@ -460,7 +463,7 @@ impl super::Nexus {
460463
)
461464
.await?;
462465

463-
Ok(Some(db_silo_user))
466+
Ok(db_silo_user)
464467
}
465468

466469
// Silo user passwords
@@ -587,7 +590,7 @@ impl super::Nexus {
587590
opctx: &OpContext,
588591
silo_lookup: &lookup::Silo<'_>,
589592
credentials: params::UsernamePasswordCredentials,
590-
) -> Result<Option<db::model::SiloUser>, Error> {
593+
) -> Result<db::model::SiloUser, Error> {
591594
let (authz_silo, _) = self.local_idp_fetch_silo(silo_lookup).await?;
592595

593596
// NOTE: It's very important that we not bail out early if we fail to
@@ -617,9 +620,11 @@ impl super::Nexus {
617620
"passed password verification without a valid user"
618621
);
619622
let db_user = fetch_user.unwrap().1;
620-
Ok(Some(db_user))
623+
Ok(db_user)
621624
} else {
622-
Ok(None)
625+
Err(Error::Unauthenticated {
626+
internal_message: "Failed password verification".to_string(),
627+
})
623628
}
624629
}
625630

nexus/src/external_api/http_entrypoints.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7399,7 +7399,7 @@ impl NexusExternalApi for NexusExternalApiImpl {
73997399
let user =
74007400
nexus.login_local(&opctx, &silo_lookup, credentials).await?;
74017401

7402-
let session = nexus.create_session(opctx, user).await?;
7402+
let session = nexus.session_create(opctx, &user).await?;
74037403
let mut response = HttpResponseHeaders::new_unnamed(
74047404
HttpResponseUpdatedNoContent(),
74057405
);

nexus/tests/integration_tests/silos.rs

+6-17
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,6 @@ async fn test_silo_admin_group(cptestctx: &ControlPlaneTestContext) {
330330
},
331331
)
332332
.await
333-
.unwrap()
334333
.unwrap();
335334

336335
let group_memberships = nexus
@@ -824,13 +823,12 @@ async fn test_silo_user_provision_types(cptestctx: &ControlPlaneTestContext) {
824823
groups: vec![],
825824
},
826825
)
827-
.await
828-
.unwrap();
826+
.await;
829827

830828
if test_case.expect_user {
831-
assert!(existing_silo_user.is_some());
829+
assert!(existing_silo_user.is_ok());
832830
} else {
833-
assert!(existing_silo_user.is_none());
831+
assert!(existing_silo_user.is_err());
834832
}
835833

836834
NexusRequest::object_delete(&client, &"/v1/system/silos/test-silo")
@@ -1063,7 +1061,6 @@ async fn test_silo_groups_jit(cptestctx: &ControlPlaneTestContext) {
10631061
},
10641062
)
10651063
.await
1066-
.unwrap()
10671064
.unwrap();
10681065

10691066
let group_memberships = nexus
@@ -1137,7 +1134,6 @@ async fn test_silo_groups_fixed(cptestctx: &ControlPlaneTestContext) {
11371134
},
11381135
)
11391136
.await
1140-
.unwrap()
11411137
.unwrap();
11421138

11431139
let group_memberships = nexus
@@ -1193,7 +1189,6 @@ async fn test_silo_groups_remove_from_one_group(
11931189
},
11941190
)
11951191
.await
1196-
.unwrap()
11971192
.unwrap();
11981193

11991194
// Check those groups were created and the user was added
@@ -1236,7 +1231,6 @@ async fn test_silo_groups_remove_from_one_group(
12361231
},
12371232
)
12381233
.await
1239-
.unwrap()
12401234
.unwrap();
12411235

12421236
let group_memberships = nexus
@@ -1306,7 +1300,6 @@ async fn test_silo_groups_remove_from_both_groups(
13061300
},
13071301
)
13081302
.await
1309-
.unwrap()
13101303
.unwrap();
13111304

13121305
// Check those groups were created and the user was added
@@ -1349,7 +1342,6 @@ async fn test_silo_groups_remove_from_both_groups(
13491342
},
13501343
)
13511344
.await
1352-
.unwrap()
13531345
.unwrap();
13541346

13551347
let group_memberships = nexus
@@ -1418,8 +1410,7 @@ async fn test_silo_delete_clean_up_groups(cptestctx: &ControlPlaneTestContext) {
14181410
},
14191411
)
14201412
.await
1421-
.expect("silo_user_from_authenticated_subject")
1422-
.unwrap();
1413+
.expect("silo_user_from_authenticated_subject");
14231414

14241415
// Delete the silo
14251416
NexusRequest::object_delete(&client, &"/v1/system/silos/test-silo")
@@ -1501,8 +1492,7 @@ async fn test_ensure_same_silo_group(cptestctx: &ControlPlaneTestContext) {
15011492
},
15021493
)
15031494
.await
1504-
.expect("silo_user_from_authenticated_subject 1")
1505-
.unwrap();
1495+
.expect("silo_user_from_authenticated_subject 1");
15061496

15071497
// Add the first user with a group membership
15081498
let _silo_user_2 = nexus
@@ -1516,8 +1506,7 @@ async fn test_ensure_same_silo_group(cptestctx: &ControlPlaneTestContext) {
15161506
},
15171507
)
15181508
.await
1519-
.expect("silo_user_from_authenticated_subject 2")
1520-
.unwrap();
1509+
.expect("silo_user_from_authenticated_subject 2");
15211510

15221511
// TODO-coverage were we intending to verify something here?
15231512
}

0 commit comments

Comments
 (0)