2424from .. import Config as CONFIG
2525from ..TestHelper import get_default_ip , do_request
2626from ..IS04Utils import IS04Utils
27+ from ..IS10Utils import IS10Utils
28+ from .Auth import PRIMARY_AUTH
2729
2830
2931class Node (object ):
@@ -39,6 +41,7 @@ def reset(self):
3941 self .receivers = {}
4042 self .senders = {}
4143 self .patched_sdp = {}
44+ self .auth_cache = {}
4245
4346 def get_sender (self , media_type = "video/raw" , version = "v1.3" ):
4447 protocol = "http"
@@ -360,11 +363,49 @@ def patch_staged(self, resource, resource_id, request_json):
360363
361364 return response_data , response_code
362365
366+ def check_authorization (self , auth , path , scope , write = False ):
367+ if not CONFIG .ENABLE_AUTH :
368+ return True , ""
369+
370+ if "Authorization" in request .headers and request .headers ["Authorization" ].startswith ("Bearer " ) \
371+ and scope in self .auth_cache and \
372+ ((write and self .auth_cache [scope ]["Write" ]) or self .auth_cache [scope ]["Read" ]):
373+ return True , ""
374+
375+ authorized , error_message = IS10Utils .check_authorization (auth ,
376+ path ,
377+ scope = scope ,
378+ write = write )
379+ if authorized :
380+ if scope not in self .auth_cache :
381+ self .auth_cache [scope ] = {"Read" : True , "Write" : write }
382+ else :
383+ self .auth_cache [scope ]["Read" ] = True
384+ self .auth_cache [scope ]["Write" ] = self .auth_cache [scope ]["Write" ] or write
385+ return authorized , error_message
386+
363387
364388NODE = Node (1 )
365389NODE_API = Blueprint ('node_api' , __name__ )
366390
367391
392+ # Authorization decorator
393+ def check_authorization (func ):
394+ def wrapper (* args , ** kwargs ):
395+ write = (request .method == 'PATCH' )
396+ authorized , error_message = NODE .check_authorization (PRIMARY_AUTH ,
397+ request .path ,
398+ scope = "x-nmos-connection" ,
399+ write = write )
400+ if authorized is not True :
401+ abort (authorized , description = error_message )
402+
403+ return func (* args , ** kwargs )
404+ # Rename wrapper to allow decoration of decorator
405+ wrapper .__name__ = func .__name__
406+ return wrapper
407+
408+
368409@NODE_API .route ('/x-nmos' , methods = ['GET' ], strict_slashes = False )
369410def x_nmos_root ():
370411 base_data = ['connection/' ]
@@ -373,27 +414,31 @@ def x_nmos_root():
373414
374415
375416@NODE_API .route ('/x-nmos/connection' , methods = ['GET' ], strict_slashes = False )
417+ @check_authorization
376418def connection_root ():
377419 base_data = ['v1.0/' , 'v1.1/' ]
378420
379421 return make_response (Response (json .dumps (base_data ), mimetype = 'application/json' ))
380422
381423
382424@NODE_API .route ('/x-nmos/connection/<version>' , methods = ['GET' ], strict_slashes = False )
425+ @check_authorization
383426def version (version ):
384427 base_data = ['bulk/' , 'single/' ]
385428
386429 return make_response (Response (json .dumps (base_data ), mimetype = 'application/json' ))
387430
388431
389432@NODE_API .route ('/x-nmos/connection/<version>/single' , methods = ['GET' ], strict_slashes = False )
433+ @check_authorization
390434def single (version ):
391435 base_data = ['senders/' , 'receivers/' ]
392436
393437 return make_response (Response (json .dumps (base_data ), mimetype = 'application/json' ))
394438
395439
396440@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/' , methods = ["GET" ], strict_slashes = False )
441+ @check_authorization
397442def resources (version , resource ):
398443 if resource == 'senders' :
399444 base_data = [r + '/' for r in [* NODE .senders ]]
@@ -404,6 +449,7 @@ def resources(version, resource):
404449
405450
406451@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>' , methods = ["GET" ], strict_slashes = False )
452+ @check_authorization
407453def connection (version , resource , resource_id ):
408454 if resource != 'senders' and resource != 'receivers' :
409455 abort (404 )
@@ -440,6 +486,7 @@ def _get_constraints(resource):
440486
441487@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>/constraints' ,
442488 methods = ["GET" ], strict_slashes = False )
489+ @check_authorization
443490def constraints (version , resource , resource_id ):
444491 base_data = [_get_constraints (resource )]
445492
@@ -472,6 +519,7 @@ def _check_constraint(constraint, transport_param):
472519
473520@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>/staged' ,
474521 methods = ["GET" , "PATCH" ], strict_slashes = False )
522+ @check_authorization
475523def staged (version , resource , resource_id ):
476524 """
477525 GET returns current staged data for given resource
@@ -515,6 +563,7 @@ def staged(version, resource, resource_id):
515563
516564@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>/active' ,
517565 methods = ["GET" ], strict_slashes = False )
566+ @check_authorization
518567def active (version , resource , resource_id ):
519568 try :
520569 if resource == 'senders' :
@@ -529,6 +578,7 @@ def active(version, resource, resource_id):
529578
530579@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>/transporttype' ,
531580 methods = ["GET" ], strict_slashes = False )
581+ @check_authorization
532582def transport_type (version , resource , resource_id ):
533583 # TODO fetch from resource info
534584 base_data = "urn:x-nmos:transport:rtp"
@@ -583,6 +633,7 @@ def node_sdp(media_type, media_subtype):
583633
584634@NODE_API .route ('/x-nmos/connection/<version>/single/<resource>/<resource_id>/transportfile' ,
585635 methods = ["GET" ], strict_slashes = False )
636+ @check_authorization
586637def transport_file (version , resource , resource_id ):
587638 # GET should either redirect to the location of the transport file or return it directly
588639 try :
0 commit comments