Skip To Content

Example: Apply service permissions from a text file

This script reads a list of permissions from a pipe-delimited text file you provide and applies the permissions to your services. You provide the list in this format:

#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—The name of a service running on ArcGIS Server. The use of / means the service is in a folder.
  • resourceType—Used only for clarification when a folder and a service have the same name. Values are service or folder.
  • permissions—A comma-separated list of ArcGIS Server roles that should receive permissions to the service. Two special roles are available: allLoggedInUsers gives permissions to anyone who has logged in with a user name from ArcGIS Server's user store, and the role public allows access to everyone whether logged in or not. These two roles only work when the server is configured with token-based authentication.

Any services not mentioned in the text file are left open for public access.

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:]))