@@ -1326,4 +1326,167 @@ mod tests {
13261326
13271327 assert_eq ! ( email. email, oidc_email) ;
13281328 }
1329+
1330+ #[ sqlx:: test( migrator = "mas_storage_pg::MIGRATOR" ) ]
1331+ async fn test_link_existing_account_when_not_allowed ( pool : PgPool ) {
1332+ #[ allow( clippy:: disallowed_methods) ]
1333+ let timestamp = chrono:: Utc :: now ( ) . timestamp_millis ( ) ;
1334+
1335+ //suffix timestamp to generate unique test data
1336+ let existing_username = format ! ( "{}{}" , "john" , timestamp) ;
1337+ let existing_email = format ! ( "{}@{}" , existing_username, "example.com" ) ;
1338+
1339+ //existing username matches oidc username
1340+ let oidc_username = existing_username. clone ( ) ;
1341+
1342+ //oidc email is different from existing email
1343+ let oidc_email: String = format ! ( "{}{}@{}" , "any_email" , timestamp, "example.com" ) ;
1344+
1345+ let subject = format ! ( "{}+{}" , "subject" , timestamp) ;
1346+
1347+ setup ( ) ;
1348+ let state = TestState :: from_pool ( pool) . await . unwrap ( ) ;
1349+ let mut rng = state. rng ( ) ;
1350+ let cookies = CookieHelper :: new ( ) ;
1351+
1352+ let claims_imports = UpstreamOAuthProviderClaimsImports {
1353+ localpart : UpstreamOAuthProviderImportPreference {
1354+ action : mas_data_model:: UpstreamOAuthProviderImportAction :: Require ,
1355+ template : None ,
1356+ } ,
1357+ email : UpstreamOAuthProviderImportPreference {
1358+ action : mas_data_model:: UpstreamOAuthProviderImportAction :: Require ,
1359+ template : None ,
1360+ } ,
1361+ ..UpstreamOAuthProviderClaimsImports :: default ( )
1362+ } ;
1363+
1364+ let id_token = serde_json:: json!( {
1365+ "preferred_username" : oidc_username,
1366+ "email" : oidc_email,
1367+ "email_verified" : true ,
1368+ } ) ;
1369+
1370+ // Grab a key to sign the id_token
1371+ // We could generate a key on the fly, but because we have one available here,
1372+ // why not use it?
1373+ let key = state
1374+ . key_store
1375+ . signing_key_for_algorithm ( & JsonWebSignatureAlg :: Rs256 )
1376+ . unwrap ( ) ;
1377+
1378+ let signer = key
1379+ . params ( )
1380+ . signing_key_for_alg ( & JsonWebSignatureAlg :: Rs256 )
1381+ . unwrap ( ) ;
1382+ let header = JsonWebSignatureHeader :: new ( JsonWebSignatureAlg :: Rs256 ) ;
1383+ let id_token = Jwt :: sign_with_rng ( & mut rng, header, id_token, & signer) . unwrap ( ) ;
1384+
1385+ // Provision a provider and a link
1386+ let mut repo = state. repository ( ) . await . unwrap ( ) ;
1387+ let provider = repo
1388+ . upstream_oauth_provider ( )
1389+ . add (
1390+ & mut rng,
1391+ & state. clock ,
1392+ UpstreamOAuthProviderParams {
1393+ issuer : Some ( "https://example.com/" . to_owned ( ) ) ,
1394+ human_name : Some ( "Example Ltd." . to_owned ( ) ) ,
1395+ brand_name : None ,
1396+ scope : Scope :: from_iter ( [ OPENID ] ) ,
1397+ token_endpoint_auth_method : UpstreamOAuthProviderTokenAuthMethod :: None ,
1398+ token_endpoint_signing_alg : None ,
1399+ id_token_signed_response_alg : JsonWebSignatureAlg :: Rs256 ,
1400+ client_id : "client" . to_owned ( ) ,
1401+ encrypted_client_secret : None ,
1402+ claims_imports,
1403+ authorization_endpoint_override : None ,
1404+ token_endpoint_override : None ,
1405+ userinfo_endpoint_override : None ,
1406+ fetch_userinfo : false ,
1407+ userinfo_signed_response_alg : None ,
1408+ jwks_uri_override : None ,
1409+ discovery_mode : mas_data_model:: UpstreamOAuthProviderDiscoveryMode :: Oidc ,
1410+ pkce_mode : mas_data_model:: UpstreamOAuthProviderPkceMode :: Auto ,
1411+ response_mode : None ,
1412+ allow_existing_users : false ,
1413+ additional_authorization_parameters : Vec :: new ( ) ,
1414+ forward_login_hint : false ,
1415+ ui_order : 0 ,
1416+ } ,
1417+ )
1418+ . await
1419+ . unwrap ( ) ;
1420+
1421+ let session = repo
1422+ . upstream_oauth_session ( )
1423+ . add (
1424+ & mut rng,
1425+ & state. clock ,
1426+ & provider,
1427+ "state" . to_owned ( ) ,
1428+ None ,
1429+ Some ( "nonce" . to_owned ( ) ) ,
1430+ )
1431+ . await
1432+ . unwrap ( ) ;
1433+
1434+ let link = repo
1435+ . upstream_oauth_link ( )
1436+ . add (
1437+ & mut rng,
1438+ & state. clock ,
1439+ & provider,
1440+ subject. clone ( ) ,
1441+ None ,
1442+ )
1443+ . await
1444+ . unwrap ( ) ;
1445+
1446+ let session = repo
1447+ . upstream_oauth_session ( )
1448+ . complete_with_link (
1449+ & state. clock ,
1450+ session,
1451+ & link,
1452+ Some ( id_token. into_string ( ) ) ,
1453+ None ,
1454+ None ,
1455+ )
1456+ . await
1457+ . unwrap ( ) ;
1458+
1459+ //create a user with an email
1460+ let user = repo
1461+ . user ( )
1462+ . add ( & mut rng, & state. clock , existing_username. clone ( ) )
1463+ . await
1464+ . unwrap ( ) ;
1465+
1466+ let _user_email = repo
1467+ . user_email ( )
1468+ . add ( & mut rng, & state. clock , & user, existing_email. clone ( ) )
1469+ . await ;
1470+
1471+ repo. save ( ) . await . unwrap ( ) ;
1472+
1473+ let cookie_jar = state. cookie_jar ( ) ;
1474+ let upstream_sessions = UpstreamSessionsCookie :: default ( )
1475+ . add ( session. id , provider. id , "state" . to_owned ( ) , None )
1476+ . add_link_to_session ( session. id , link. id )
1477+ . unwrap ( ) ;
1478+ let cookie_jar = upstream_sessions. save ( cookie_jar, & state. clock ) ;
1479+ cookies. import ( cookie_jar) ;
1480+
1481+ let request = Request :: get ( & * mas_router:: UpstreamOAuth2Link :: new ( link. id ) . path ( ) ) . empty ( ) ;
1482+ let request = cookies. with_cookies ( request) ;
1483+ let response = state. request ( request) . await ;
1484+ cookies. save_cookies ( & response) ;
1485+ response. assert_status ( StatusCode :: OK ) ;
1486+ response. assert_header_value ( CONTENT_TYPE , "text/html; charset=utf-8" ) ;
1487+
1488+ assert ! ( response
1489+ . body( ) . contains( "Unexpected error" ) ) ;
1490+
1491+ }
13291492}
0 commit comments