@@ -282,11 +282,22 @@ def test_validate_entitlements(
282282 {"identity" : {"type" : "System" , "org_id" : "123" , "system" : {}}},
283283 "Invalid identity data" ,
284284 ),
285+ # System identity without org_id is rejected (org_id is required).
285286 (
286287 {
287288 "identity" : {
288289 "type" : "System" ,
289- "org_id" : "123" ,
290+ "system" : {"cn" : "test" },
291+ }
292+ },
293+ "Invalid identity data" ,
294+ ),
295+ # System identity with empty org_id is rejected.
296+ (
297+ {
298+ "identity" : {
299+ "type" : "System" ,
300+ "org_id" : "" ,
290301 "system" : {"cn" : "test" },
291302 }
292303 },
@@ -719,9 +730,9 @@ def test_user_non_string_types_rejected(
719730 (("system" , "cn" ), True ),
720731 (("system" , "cn" ), []),
721732 (("system" , "cn" ), {}),
722- (("account_number" ,), None ),
733+ # account_number is optional: None/absent is allowed, but a present,
734+ # non-empty, non-string value is still rejected.
723735 (("account_number" ,), 12345 ),
724- (("account_number" ,), False ),
725736 (("account_number" ,), []),
726737 (("account_number" ,), {}),
727738 ],
@@ -826,3 +837,89 @@ def test_valid_user_data_still_passes(self, user_identity_data: dict) -> None:
826837 def test_valid_system_data_still_passes (self , system_identity_data : dict ) -> None :
827838 """Regression: valid System identity data passes validation."""
828839 RHIdentityData (system_identity_data )
840+
841+ @pytest .mark .parametrize ("account_number" , ["" , None ])
842+ def test_system_empty_or_absent_account_number_accepted (
843+ self , system_identity_data : dict , account_number : Optional [str ]
844+ ) -> None :
845+ """Accept System identity with empty or absent account_number.
846+
847+ No-cost RHEL developer subscriptions send an empty account_number with a
848+ populated org_id. These must authenticate successfully, falling back to
849+ the system cn as the username.
850+ """
851+ identity = system_identity_data ["identity" ]
852+ if account_number is None :
853+ identity .pop ("account_number" , None )
854+ else :
855+ identity ["account_number" ] = account_number
856+
857+ rh_identity = RHIdentityData (system_identity_data )
858+ # username falls back to the system cn when account_number is empty/absent
859+ assert rh_identity .get_username () == identity ["system" ]["cn" ]
860+ assert rh_identity .get_user_id () == identity ["system" ]["cn" ]
861+
862+ def test_system_developer_subscription_identity_accepted (self ) -> None :
863+ """Accept a real developer-subscription System identity end-to-end.
864+
865+ Mirrors the cert-auth payload sent by no-cost RHEL developer
866+ subscriptions: empty account_number, populated org_id, valid system cn.
867+ """
868+ identity_data = {
869+ "identity" : {
870+ "system" : {
871+ "cert_type" : "system" ,
872+ "cn" : "14b75b86-6f99-411d-b41d-f400268b5807" ,
873+ },
874+ "auth_type" : "cert-auth" ,
875+ "account_number" : "" ,
876+ "type" : "System" ,
877+ "org_id" : "18939564" ,
878+ "internal" : {
879+ "cross_access" : False ,
880+ "auth_time" : 0 ,
881+ "org_id" : "18939564" ,
882+ },
883+ }
884+ }
885+
886+ rh_identity = RHIdentityData (identity_data )
887+ assert rh_identity .get_username () == "14b75b86-6f99-411d-b41d-f400268b5807"
888+ assert rh_identity .get_user_id () == "14b75b86-6f99-411d-b41d-f400268b5807"
889+ assert rh_identity .get_org_id () == "18939564"
890+
891+ def test_system_present_account_number_unchanged (
892+ self , system_identity_data : dict
893+ ) -> None :
894+ """Regression: System with a present, non-empty account_number is unchanged.
895+
896+ get_username() must still return the account_number when it is present
897+ and non-empty.
898+ """
899+ rh_identity = RHIdentityData (system_identity_data )
900+ assert rh_identity .get_username () == "123"
901+
902+ @pytest .mark .parametrize ("org_id" , ["" , None ])
903+ def test_system_missing_org_id_rejected (
904+ self , system_identity_data : dict , org_id : Optional [str ]
905+ ) -> None :
906+ """Reject System identity when org_id is empty or absent.
907+
908+ org_id is the required organizational identifier for System identities.
909+ """
910+ identity = system_identity_data ["identity" ]
911+ if org_id is None :
912+ identity .pop ("org_id" , None )
913+ else :
914+ identity ["org_id" ] = org_id
915+
916+ with pytest .raises (HTTPException ) as exc_info :
917+ RHIdentityData (system_identity_data )
918+ assert exc_info .value .status_code == 400
919+
920+ def test_system_empty_cn_rejected (self , system_identity_data : dict ) -> None :
921+ """Reject System identity when system cn is empty."""
922+ system_identity_data ["identity" ]["system" ]["cn" ] = ""
923+ with pytest .raises (HTTPException ) as exc_info :
924+ RHIdentityData (system_identity_data )
925+ assert exc_info .value .status_code == 400
0 commit comments