Skip To Content

Exemple : appliquer des autorisations de service à partir d'un fichier texte

Ce script permet de lire une liste d'autorisations dans un fichier texte délimité par des barres verticales et d'appliquer les autorisations à vos services. Vous fournissez la liste au format suivant :

#resourceName|resourceType|permissions

resourceName=Rainier/Fauna|permissions=rangers,volunteers
resourceName=Roads|permissions=public
resourceName=BufferGeoprocessing|permissions=rangers,volunteers,visitors
resourceName=Geocode|permissions=rangers,volunteers,visitors
resourceName=ServiceArea|permissions=allLoggedInUsers
resourceName=Yosemite|resourceType=folder|permissions=visitors
resourceName=Parks/Yosemite|resourceType=service|permissions=rangers

  • resourceName : nom d'un service qui exécuté sur ArcGIS Server. L'utilisation du symbole / signifie que le service se trouve dans un dossier.
  • resourceType : n'est utilisé qu'à des fins de clarification lorsqu'un dossier et un service portent le même nom. Les valeurs possibles sont service ou folder.
  • permissions : liste séparée par des virgules de rôles ArcGIS Server qui doivent recevoir des autorisations d'accès au service. Deux rôles spéciaux sont disponibles : allLoggedInUsers accorde des autorisations à quiconque est connecté avec un nom d'utilisateur provenant du magasin d'utilisateurs d'ArcGIS Server et le rôle public accorde des autorisations d'accès à tout utilisateur, qu'il soit ou non connecté. Ces deux rôles ne fonctionnent que lorsque le serveur est configuré avec une authentification à base de jetons.

Les services non indiqués dans le fichier texte restent ouverts à tout accès public.

import json, urllib,httplib

import sys

import getpass

import codecs

def main(argv=None):
    
    # Ask for admin user name and password
    username = raw_input("Enter user name: ")
    password = getpass.getpass("Enter password: ")
    
    # Ask for server name & port
    serverName = raw_input("Enter server name: ")
    serverPort = raw_input("Enter server port: ")
    
    # Get a token and connect
    token = getToken(username, password, serverName, serverPort)
    
    if token == "":
        sys.exit(1)

    # Input file that contains the resources and permissions
    permissionsFile = raw_input("Path to comma-delimited text file containing resources and permissions: ")

    permissions = {}
    num = 0

    for permissionsRow in readlinesFromInputFile(permissionsFile):
                 
            permissionsEntry = {}
            
            for index in range(len(permissionsRow)):
                
                permissionsProp = permissionsRow[index].split("=")
                
                if permissionsProp[0] == "resourceName":
                    permissionsEntry["resourceName"] = permissionsProp[1]
                if permissionsProp[0] == "resourceType":
                    permissionsEntry["resourceType"] = permissionsProp[1]
                if permissionsProp[0] == "permissions":
                    permissionsEntry["permissions"] = permissionsProp[1]
             
                # Add the services information to a dictionary
                permissions["resource" + str(num)] = permissionsEntry
            
            num +=1

    # Call helper function to add permissions on the given resources
    assignPermissions(permissions,serverName,serverPort,token)

# A function that reads lines from the input file
def readlinesFromInputFile(filename, delim='|'):
    file = codecs.open(filename,'r','utf-8-sig')
    for line in file.readlines():
        # Remove the trailing whitespaces and the newline characters
        line = line.rstrip()
        
        if line.startswith('#') or len(line) == 0:
            pass # Skip the lines that contain # at the beginning or any empty lines
        else:
            # Split the current line into list
            yield line.split(delim)
    file.close()
    
def assignPermissions(permissionsDict,serverName,serverPort,token):
    
    for permissionToAdd in permissionsDict:
        
        permissionRow = permissionsDict[permissionToAdd]
        
        if ((not 'resourceName' in permissionRow) or (not 'permissions' in permissionRow)):
            # either resourceName or permissions are missing from the row, so skipping the row
            continue
            
        
        if 'resourceType' in permissionRow: # User provided the resourceType in the input file
            resourceType = permissionRow['resourceType']
        else: # User did not provide the resourceType in the input file
            resourceType = ""
            
        ## Permissions (Roles) are a comma separated list, we need to split the permissions(roles) using a comma
        permissions = permissionRow['permissions'].split(',')
        
        # Check if the special role "public" is specified
        if 'public' in permissions:
            if (isTokenBasedAuth): # public special role is applicable only when token based authentication is configured on the server
                applyPermission(permissionRow['resourceName'],resourceType,'public',True,token,serverName,serverPort)
            continue
        
        # Check if the special role "allLoggedInUsers" is specified
        if 'allLoggedInUsers' in permissions:
            if (isTokenBasedAuth): # allLoggedInUsers special role applicable only when token based authentication is configured on the server
                applyPermission(permissionRow['resourceName'],resourceType,'allLoggedInUsers',True,token,serverName,serverPort)
                # remove public access on the resource if present
                if (checkIfRolePresentOnResource(permissionRow['resourceName'],resourceType,'public',token,serverName,serverPort)):         
                    applyPermission(permissionRow['resourceName'],resourceType,'public',False,token,serverName,serverPort)
            continue
        
        # Loop through all the roles (in a comma separated list of  roles/permissions) and add them to the resources
        for index in range(len(permissions)):
            role = permissions[index]
            if (checkIfRoleExistsInRoleStore(role,token,serverName,serverPort)):
                applyPermission(permissionRow['resourceName'],resourceType,role,True,token,serverName,serverPort)
            else:
                print "The permission '" + role + "' does not exist in the server."
                continue    
        
        # Verify that at least one of the roles in the list of roles is added to the resource successfully, if yes we can remove public/allLoggedInUsers (if present) roles from the resource
        appliedPermissionSuccessfully = False
        for index in range(len(permissions)):
            permission = permissions[index]
            if (checkIfRolePresentOnResource(permissionRow['resourceName'],resourceType,permission,token,serverName,serverPort)):
                # We verified that at least one permission has been applied successfully, so we can break and check/remove public access from the resource
                appliedPermissionSuccessfully = True
                break         
        
        if (appliedPermissionSuccessfully):        
            # remove public access on the resource if present
            if (checkIfRolePresentOnResource(permissionRow['resourceName'],resourceType,'public',token,serverName,serverPort)):         
                applyPermission(permissionRow['resourceName'],resourceType,'public',False,token,serverName,serverPort)
            
            # remove allLoggedInUsers role on the resource if present
            if (checkIfRolePresentOnResource(permissionRow['resourceName'],resourceType,'allLoggedInUsers',token,serverName,serverPort)):           
                applyPermission(permissionRow['resourceName'],resourceType,'allLoggedInUsers',False,token,serverName,serverPort)
        
def applyPermission(resourceName,resourceType,role,isAllowed,token,serverName,serverPort):
        
    if (role == 'public'):
        params = urllib.urlencode({'token':token,'f':'json','principal':'esriEveryone','isAllowed':isAllowed})
    else:
        if (role == 'allLoggedInUsers'):
            params = urllib.urlencode({'token':token,'f':'json','principal':'esriAuthenticated','isAllowed':isAllowed})
        else:
            params = urllib.urlencode({'token':token,'f':'json','principal':role,'isAllowed':isAllowed})
                
    ## Find the resource URL
    resourceURL = None
    
    if (resourceType == ""): # User did not provide the resourceType, so we need to find out by making admin calls
        resourceTypeAndURL = getResourceTypeAndURL(resourceName,serverName,serverPort,token)
        resourceURL = resourceTypeAndURL[1]
        resourceType = resourceTypeAndURL[0]
    else: ## this means user has explicitly provided the resourceType in the input file 
        if (resourceType == 'folder'):
            resourceURL = "/arcgis/admin/services/"+resourceName+"/permissions/"
        elif(resourceType == 'service'):
            ## it is a service, so we need to find its type
            serviceType = getServiceType(resourceName,serverName,serverPort,token)
        
            if serviceType is not None:   
                resourceURL = "/arcgis/admin/services/"+resourceName+"."+serviceType+"/permissions/"          
    
    if (resourceURL is not None): 
        resourceURL = resourceURL+"/add"
        
        response, data = postToServer(serverName, serverPort, resourceURL, params)
                
        if (response.status != 200 or not assertJsonSuccess(data)):
           if (isAllowed): 
               print "Unable to assign the permission '" + role + "' on the " + resourceType + " '" + resourceName + "'"
               print str(data)
        else:
           if (isAllowed):
               print "Successfully assigned the permission '" + role + "' on the " + resourceType + " '" + resourceName + "'"  
        
    else:
         print "Unable to find the resource: '" + resourceName + "' on the server."

def checkIfRolePresentOnResource(resourceName,resourceType,role,token,serverName,serverPort):
            
    params = urllib.urlencode({'token':token,'f':'json'})
    
    if (role == 'public'):
        role = 'esriEveryone'
    elif (role == 'allLoggedInUsers'):
        role = 'esriAuthenticated'    
    
    ## Find the resource URL
    resourceURL = None
    
    if (resourceType == ""): # User did not provide the resourceType, so we need to find out by making admin calls
        resourceTypeAndURL = getResourceTypeAndURL(resourceName,serverName,serverPort,token)
        resourceURL = resourceTypeAndURL[1]
        resourceType = resourceTypeAndURL[0]
    else: ## this means user has explicitly provided the resourceType in the input file 
        if (resourceType == 'folder'):
            resourceURL = "/arcgis/admin/services/"+resourceName+"/permissions/"
        elif(resourceType == 'service'):
            ## it is a service, so we need to find its type
            serviceType = getServiceType(resourceName,serverName,serverPort,token)
        
            if serviceType is not None:   
                resourceURL = "/arcgis/admin/services/"+resourceName+"."+serviceType+"/permissions/"          
    
    if (resourceURL is not None): 

        response, data = postToServer(serverName, serverPort, resourceURL, params)
        
        if (response.status != 200 or not assertJsonSuccess(data)):
            print "Unable to check if the permission '" + role + "' exists on the " + resourceType + " '" + resourceName + "'"
            print str(data)
            return
        else:
            prmsJSON = json.loads(data)
            prms = prmsJSON['permissions']
            for prm in prms :
                if (('principal' in prm) and (codecs.decode(prm['principal'],'utf-8') == role)):
                    return True
            return False

def checkIfRoleExistsInRoleStore(role,token,serverName,serverPort):
            
    params = urllib.urlencode({'token':token,'f':'json'})
    
    ## Find the resource URL
    resourceURL = "/arcgis/admin/security/roles/getRoles"

    response, data = postToServer(serverName, serverPort, resourceURL, params)
    
    if (response.status != 200 or not assertJsonSuccess(data)):
        print "Unable to check if the permission '" + role + "' exists on the server"
        print str(data)
        return
    else:
        rolesJSON = json.loads(data)
        roles = rolesJSON['roles']
        for roleDict in roles :
            if (('rolename' in roleDict) and (codecs.decode(roleDict['rolename'],'utf-8') == role)):
                return True
        return False
            
def getResourceTypeAndURL(resourceName,serverName,serverPort,token):
    
    resourceURL = None
    resourceType = None
    
    if isItFolder(resourceName,serverName,serverPort,token):
        resourceURL = "/arcgis/admin/services/"+resourceName+"/permissions"
        resourceType = 'folder'
    else:
        # Check if the resource is a service - it is possible to have the same name for service and folder
        #  and if a service and a folder have same name, we apply the same permissions on both.
        serviceType = getServiceType(resourceName,serverName,serverPort,token)
        
        if serviceType is not None:   
            resourceURL = "/arcgis/admin/services/"+resourceName+"."+serviceType+"/permissions"    
            resourceType = 'service'      
    
    return [resourceType,resourceURL]

# Helper function that checks to see if the authentication mode is token based
def isTokenBasedAuth(serverName, serverPort, token):
    
    securityConfigURL = "/arcgis/admin/security/config/"
    params = urllib.urlencode({'token':token,'f':'json'})

    response, data = postToServer(serverName, serverPort, securityConfigURL, params)
    
    if (response.status != 200 or not assertJsonSuccess(data)):
        print "Unable to determine if the server has token-based authentication or not."
        print str(data)
        return
        
    # Extract the security store type
    securityConfig = json.loads(data)

    if (str(securityConfig['authenticationMode']) != "ARCGIS_TOKEN"):
        return False

    return True

# A function that will post HTTP POST request to the server
def postToServer(serverName, serverPort, url, params):
    
    httpConn = httplib.HTTPConnection(serverName, serverPort)
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}

    # URL encode the resource URL
    url = urllib.quote(url.encode('utf-8'))
    
    # Build the connection to add the roles to the server
    httpConn.request("POST", url, params, headers)

    response = httpConn.getresponse()
    data = response.read()
    httpConn.close()

    return (response, data)
    
# A function that checks that the JSON response received from the server does not contain an error   
def assertJsonSuccess(data):
    obj = json.loads(data)
    if 'status' in obj and obj['status'] == "error":
        return False
    else:
        return True

def getToken(username, password, serverName, serverPort):

    tokenURL = "/arcgis/admin/generateToken"
    
    params = urllib.urlencode({'username': username, 'password': password,'client': 'requestip', 'f': 'json'})
    
    response, data = postToServer(serverName, serverPort, tokenURL, params)
        
    if (response.status != 200 or not assertJsonSuccess(data)):
        print "Error while fetching tokens from admin URL. Please check if the server is running and ensure that the username/password provided are correct"
        print str(data)
        return ""
    else: 
        # Extract the token from it
        token = json.loads(data)   
        return token['token']            

# Checks if the given resource is a folder or not
def isItFolder(resourceName,serverName,serverPort,token):
    
    params = urllib.urlencode({'token':token,'f': 'json'})
    
    folderURL = "/arcgis/admin/services"
    
    response, data = postToServer(serverName, serverPort, folderURL, params)
    
    if (response.status != 200 or not assertJsonSuccess(data)):
        print "Error while fetching folders from the server."
        print str(data)
        return
    else:
        # Check if the resource is a folder
        rootJSON = json.loads(data)
        
        folders = rootJSON['folders']
        for folder in folders:
            if codecs.decode(folder,'utf-8') == resourceName :
                return True
        return False      
    
# Checks if the resource is a service; if it is a service it returns the service type; otherwise, it returns None
def getServiceType(resourceName,serverName,serverPort,token):
    
    params = urllib.urlencode({'token':token,'f': 'json'})
    
    headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
    
    folderSeparator = resourceName.find("/")
    
    if  folderSeparator == -1 :
        # This service is probably present at the root folder
        URL = "/arcgis/admin/services/"
        serviceName = resourceName
    else:
        URL = "/arcgis/admin/services/"+resourceName[0:folderSeparator]
        serviceName = resourceName[folderSeparator+1:]
    
    response, data = postToServer(serverName, serverPort, URL, params)
    
    if (response.status != 200 or not assertJsonSuccess(data)):
        print "Error while fetching the service type from the server."
        print str(data)
        return
    else:
        rootJSON = json.loads(data)
        
        services = rootJSON['services']
        for service in services:
            if codecs.decode(service['serviceName'],'utf-8') == serviceName : 
                return service['type']
            
        return None
    
if __name__ == "__main__" :
    sys.exit(main(sys.argv[1:]))