@@ -888,6 +888,116 @@ describe("deviceHandlers", () => {
888888 ) ;
889889 } ) ;
890890
891+ it ( "allows non-device operator sessions to approve operator roles within caller scopes" , async ( ) => {
892+ getPendingDevicePairingMock . mockResolvedValue ( {
893+ requestId : "req-1" ,
894+ deviceId : "device-2" ,
895+ publicKey : "pk-2" ,
896+ role : "operator" ,
897+ roles : [ "operator" ] ,
898+ scopes : [ "operator.pairing" ] ,
899+ ts : 100 ,
900+ } ) ;
901+ approveDevicePairingMock . mockResolvedValue ( {
902+ status : "approved" ,
903+ requestId : "req-1" ,
904+ device : {
905+ deviceId : "device-2" ,
906+ publicKey : "pk-2" ,
907+ role : "operator" ,
908+ roles : [ "operator" ] ,
909+ approvedScopes : [ "operator.pairing" ] ,
910+ approvedAtMs : 100 ,
911+ createdAtMs : 50 ,
912+ } ,
913+ } ) ;
914+ const opts = createOptions (
915+ "device.pair.approve" ,
916+ { requestId : "req-1" } ,
917+ { client : createClient ( [ "operator.pairing" ] ) } ,
918+ ) ;
919+
920+ await deviceHandlers [ "device.pair.approve" ] ( opts ) ;
921+
922+ expect ( getPendingDevicePairingMock ) . toHaveBeenCalledWith ( "req-1" ) ;
923+ expect ( approveDevicePairingMock ) . toHaveBeenCalledWith ( "req-1" , {
924+ callerScopes : [ "operator.pairing" ] ,
925+ } ) ;
926+ expect ( opts . respond ) . toHaveBeenCalledWith (
927+ true ,
928+ {
929+ requestId : "req-1" ,
930+ device : {
931+ deviceId : "device-2" ,
932+ publicKey : "pk-2" ,
933+ role : "operator" ,
934+ roles : [ "operator" ] ,
935+ approvedAtMs : 100 ,
936+ createdAtMs : 50 ,
937+ tokens : undefined ,
938+ } ,
939+ } ,
940+ undefined ,
941+ ) ;
942+ } ) ;
943+
944+ it ( "rejects approving node roles from non-admin shared-auth sessions" , async ( ) => {
945+ getPendingDevicePairingMock . mockResolvedValue ( {
946+ requestId : "req-1" ,
947+ deviceId : "device-2" ,
948+ publicKey : "pk-2" ,
949+ role : "node" ,
950+ roles : [ "node" ] ,
951+ ts : 100 ,
952+ } ) ;
953+ const opts = createOptions (
954+ "device.pair.approve" ,
955+ { requestId : "req-1" } ,
956+ { client : createClient ( [ "operator.pairing" ] ) } ,
957+ ) ;
958+
959+ await deviceHandlers [ "device.pair.approve" ] ( opts ) ;
960+
961+ expect ( approveDevicePairingMock ) . not . toHaveBeenCalled ( ) ;
962+ expectRespondedErrorMessage ( opts , "device pairing approval denied" ) ;
963+ } ) ;
964+
965+ it ( "rejects approving mixed operator and node roles from non-admin sessions" , async ( ) => {
966+ getPendingDevicePairingMock . mockResolvedValue ( {
967+ requestId : "req-1" ,
968+ deviceId : "device-2" ,
969+ publicKey : "pk-2" ,
970+ role : "operator" ,
971+ roles : [ " operator " , " node " ] ,
972+ scopes : [ "operator.pairing" ] ,
973+ ts : 100 ,
974+ } ) ;
975+ const opts = createOptions (
976+ "device.pair.approve" ,
977+ { requestId : "req-1" } ,
978+ { client : createClient ( [ "operator.pairing" ] ) } ,
979+ ) ;
980+
981+ await deviceHandlers [ "device.pair.approve" ] ( opts ) ;
982+
983+ expect ( approveDevicePairingMock ) . not . toHaveBeenCalled ( ) ;
984+ expectRespondedErrorMessage ( opts , "device pairing approval denied" ) ;
985+ } ) ;
986+
987+ it ( "denies unknown approvals from non-admin non-device sessions" , async ( ) => {
988+ getPendingDevicePairingMock . mockResolvedValue ( null ) ;
989+ const opts = createOptions (
990+ "device.pair.approve" ,
991+ { requestId : "missing" } ,
992+ { client : createClient ( [ "operator.pairing" ] ) } ,
993+ ) ;
994+
995+ await deviceHandlers [ "device.pair.approve" ] ( opts ) ;
996+
997+ expect ( approveDevicePairingMock ) . not . toHaveBeenCalled ( ) ;
998+ expectRespondedErrorMessage ( opts , "device pairing approval denied" ) ;
999+ } ) ;
1000+
8911001 it ( "rejects approving node roles for the caller device without admin scope" , async ( ) => {
8921002 getPendingDevicePairingMock . mockResolvedValue ( {
8931003 requestId : "req-1" ,
0 commit comments