#!/usr/bin/ruby -w require "net/http" require "uri" require "cgi" require "json" require "csv" #------------------------------------------------------------------------------------------------------- # Developed by Rakesh Kapoor for creating/Updating/Deleting Access Profiles and Roles using a CSV file # Version 8.0 # It uses a Personal Access Token therefore you will need clientId and clientSecret for generation this token in config file. # Authorization token is renewed after every 3300 seconds # Now script is not case sensitive to the name of Source,Roles Entitlements and accessProfiles # Created on 03/10/2017, Modified on 05/02/2017 # Updated on 08/29/2017 Now it can add Owner of AccessProfile and # Updated on 09/05/2017 New APIs for Create/update and list of AccessProfile are used from https://api.identitynow.com # Updated on 8/20/2019 New APIs to generate list of AccessProfile and list of Entitlements are used # Updated on 12/01/2020 New V2 APIs are used for Create/Update/Delete Roles and Create/Update/Delete Access Profiles in version 6.5. # Updated on 12/01/2020 Added support for Governance groups in version 6.5. # Updated on 03/08/2021 Added support for requestable Roles with approval from manager, owner and Governance groups, # Updated on 06/26/2021 by Chirag Patel, fixed a bug causing error for source names containing common strings in a given complex criteria. # Updated on 06/27/2021 Minor issue of not handling Group Owners for updating accessProfile was fixed. # Updated on 07/19/2021 revokeRequestApprovalSchemes has been added in this version 8.0. # new API use default limit of 250, program figure out total number and update default limit to actual number # of Access Profiles or Entitlements. # Error Access Profile not found, or Entitlement not found has been taken care in the version 5.5. # keeps membership criteria for an existing role while updating if role already exist, # earlier it used to get wiped out and an extra line was needed to update it. # For membership another type of value IDENTITY_LIST has been added now you can give two types of membership # IDENTITY_LIST and VALUE_MAP, for IDENTITY_LIST, Column 4 in CSV file will be a JSON list of Identities (Account IDs) while # for VALUE_MAP it will be JSON MAP of selection criteria as described in README file # Updated on 12/20/2017 Now It can add Approvers to the Access Profile and option of adding # When User Requests or When Approver Denies # Approvers are given as a text list like "manager,sourceOwner,accessProfileOwner,appOwner" # Currently above four types of approvers are supported you can give any number out of four. # Now it can also update owner of the Access Profile. # COMPLEX_CRITERIA has been added in version 5 # Personal Authentication token is required and config.json file is changed in version 6.0, No limit to load # Entitlements/accessProfiles and roles. #-------------------------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------- # Start of Methods #----------------------------------------------------------------------------------- def create_header(url,user,pass) # This method generates basic authorization token and then use it in the header # It returns header # header = nil header = {"Content-Type" => "application/json"} uri = URI.parse( "#{url}/oauth/token?grant_type=client_credentials&client_id=#{user}&client_secret=#{pass}" ) # puts "URI: #{uri}" http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Post.new( uri.request_uri, header ) # request.basic_auth( apiUser, apiKey ) response = http.request( request ) session = JSON.parse( response.body ) # puts "Response Body #{response}" case response when Net::HTTPSuccess header = { "Authorization" => "Bearer #{session['access_token']}", "X-CSRF-Token" => "nocheck" } puts "Authentication successful." # puts "Session Token: #{session['access_token']}" when Net::HTTPUnauthorized puts "Authentication Failed: Unauthorized." when Net::HTTPServerError puts "Authentication Failed: Server Error." else puts "Authentication Failed: #{session['error']}" end return header end def generate_sourceVsIdLists(url,header) # This method pulls list of existing sources and generates two Hash maps # one SourceName Vs Id and second sourceName vs. source Id # These Hash maps are used to get Id or sourceId respectively using the name of the source # sourceName is used in creation or update of Access Profile. # It returns list of two hash Maps sourceNameVsId = Hash.new sourceNameVsSourceId = Hash.new #sourceNameVsSourceIdRegular is created for replacing sourceName with sourceId in COMPLEX_CRITERIA assignedValue sourceNameVsSourceIdRegular = Hash.new #uri = URI.parse( "#{url}/cc/api/source/list?start=0&limit=1000" ) uri = URI.parse( "#{url}/beta/sources?count=true" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) responseBody = JSON.parse( response.body, symbolize_names: true ) count = response['X-Total-Count'].to_i #puts count offset = 50 while(offset < count + 50) uri = URI.parse( "#{url}/beta/sources?offset=#{offset}" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) responseBody = responseBody + JSON.parse( response.body, symbolize_names: true ) offset = offset + 50 end case response when Net::HTTPSuccess # Create a HashMap of name vs. ID named sourceNameVsId of all the sources from obtained list of Sources # Create a HashMap of name vs. sourceId named sourceNameVsSourceId of all the sources from obtained list of Sources # These lists will subsequently be used to get ID and sourceId of a source based on its name responseBody.each {|a| sourceNameVsId["#{a[:name].upcase}"] = a[:id]; puts a[:name];sourceNameVsSourceId["#{a[:name].upcase}"] = a[:connectorAttributes][:cloudExternalId]; sourceNameVsSourceIdRegular["#{a[:name].upcase}"] = a[:id]} # puts sourceNameVsId # puts sourceNameVsSourceId puts "Source Hash Maps creation Success!" when Net::HTTPUnauthorized puts "Source Hash Maps creation Failed: Unauthorized." when Net::HTTPServerError puts "Source Hash Maps creation Failed: Server Error." else puts "Source Hash Maps creation Failed: #{response.code}" end return sourceNameVsId,sourceNameVsSourceId,sourceNameVsSourceIdRegular end def getRoleList(url, header,limit, offset) # This method pulls list of existing roles and create a hsh Map of displayName vs Id # Returns a hash Map rolesHash = Hash.new #uri = URI.parse( "#{url}/cc/api/role/list?start=0&limit=#{limit}" ) #only invoke count for the first request as count seems to have a performance impact if(offset==0) uri = URI.parse( "#{url}/beta/roles?limit=#{limit}&offset=#{offset}&count=true" ) else uri = URI.parse( "#{url}/beta/roles?limit=#{limit}&offset=#{offset}" ) end http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) case response when Net::HTTPSuccess # Get list of all the existing Roles # These list will subsequently be used to get ID of Roles based on its name responseBody = JSON.parse( response.body ) # puts responseBody when Net::HTTPUnauthorized puts "Failed to get Role List: Unauthorized." when Net::HTTPServerError puts "Failed to get Role List: Server Error." else puts "Failed to get Role List: #{JSON.parse( response.body )}" end return response end def generate_roleDisplayNameVsIdMap(url,header) # This method pulls list of existing roles and create a hsh Map of displayName vs Id # Returns a hash Map limit = 50 offset = 0 rolesHash = Hash.new response = getRoleList(url,header,limit,offset) responseBody = JSON.parse( response.body ) # puts "Response Body #{responseBody}" count = response['X-Total-Count'].to_i puts "Response Body total count #{count}" while(offset < count + 50) response = getRoleList(url,header,limit,offset) responseBody = responseBody + JSON.parse( response.body ) offset = offset + 50 end puts "Total number of Roles: #{count}" sourceList = responseBody sourceList.each {|a| rolesHash["#{a['name'].upcase()}"] = a['id']} # puts rolesHash puts "Role Hash Map creation Success!" return rolesHash end def getAccessProfileList(url,header,limit,offset) # This method pulls list of existing access Profiles with given upper limit #uri = URI.parse( "#{url}/cc/api/accessProfile/list?start=0&limit=#{limit}" ) #only invoke count for the first request as count seems to have a performance impact if(offset==0) uri = URI.parse( "#{url}/beta/access-profiles?limit=#{limit}&offset=#{offset}&count=true" ) else uri = URI.parse( "#{url}/beta/access-profiles?limit=#{limit}&offset=#{offset}" ) end http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) case response when Net::HTTPSuccess # Get list of all the existing accessProfiles # This list will subsequently be used to get ID of an accessProfile based on its name responseBody = JSON.parse( response.body ) # puts responseBody when Net::HTTPUnauthorized puts "Failed to get Access Profile List: Unauthorized." when Net::HTTPServerError puts "Failed to get Access Profile List: Server Error." else puts "Failed to get Access Profile List: #{response.code}" end #return responseBody return response end def generate_accessProfileNameVsIdMap(url,header) # This method create a hash map of name vs. Id limit = 50 offset = 0 accessProfilesHash = Hash.new response = getAccessProfileList(url,header,limit,offset) responseBody = JSON.parse( response.body ) # puts "Response Body #{responseBody}" count = response['X-Total-Count'].to_i puts "Response Body total count #{count}" while(offset < count + 50) response = getAccessProfileList(url,header,limit,offset) responseBody = responseBody + JSON.parse( response.body ) offset = offset + 50 puts "Offset: #{offset}" end puts "Total number of Access Profiles: #{count}" # Create a HashMap of name vs. ID named accessProfilesHash of all the existing accessProfiles # These list will subsequently be used to get ID of an accessProfile based on its name sourceList = responseBody # puts sourceList sourceList.each {|a| accessProfilesHash["#{a['name'].upcase()}"] = a['id']} # puts accessProfilesHash puts "Access Profile Hash Map Creation Success!" return accessProfilesHash end def getEntitlementList(url,header,source,cursor) # This method pulls list of existing entitlements of a given source # This list is uses to create a hsh Map of displayableName vs Id # Returns a hash Map # uri = URI.parse( "#{url}/cc/api/entitlement/list?limit=#{limit}&CISApplicationId=#{source}" ) # request entitlements with cursor to traverse through all and request cursor if cursor is nil(false) if (cursor) uri = URI.parse( "#{url}/beta/entitlements?filters=source.id eq \"#{source}\"&cursor=#{cursor}" ) else uri = URI.parse( "#{url}/beta/entitlements?filters=source.id eq \"#{source}\"&cursor" ) end puts uri http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) case response when Net::HTTPSuccess responseBody = JSON.parse(response.body) # puts responseBody when Net::HTTPUnauthorized puts "#{source} Entitlements Hash Map creation is a Failure!" puts "Error: Unauthorized." when Net::HTTPServerError puts "#{source} Entitlements Hash Map creation is a Failure!" puts "Error: Server Error." else puts "#{source} Entitlements Hash Map creation is a Failure!" puts "Error: #{response.code}" end return response end def generate_governanceGroupDisplayNameVsIDMap(url, header) # This method creates a HashMap of governance group names to their ids. # This map is referenced if a governance group is listed as an approver. groupHash = Hash.new uri = URI.parse("#{url}/v2/workgroups") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true request = Net::HTTP::Get.new(uri.request_uri, header ) response = http.request( request ) case response when Net::HTTPSuccess groupList = JSON.parse( response.body ) puts "List of Goverance Groups: #{response.body}" # Build the governance group HashMap. groupList.each {|a| groupHash["#{a['name']}"] = a['id']} puts "Governance Group Hash Map creation success!" when Net::HTTPUnauthorized puts "Governance Group Hash Map creation Failed: Unauthorized." when Net::HTTPServerError puts "Governance Group Hash Map creation Failed: Server Error." else puts "Governance Hash Map creation Failed: #{json['error']}" end return groupHash end def generate_entitlementDisplayNameVsIdMap(url,header,source) # This method pulls list of existing entitlements of a given source # This list is uses to create a hsh Map of displayableName vs Id # Returns a hash Map # Default limit is set to 250 if total entitlements count is more it gets the list again cursor = nil entitlementHash = Hash.new response = getEntitlementList(url,header,source,cursor) responseBody = JSON.parse( response.body ) # puts "Response Body #{responseBody}" # count = responseBody['count'] ## figure out how to loop up cursor = response['Slpt-Cursor'] while(cursor) response = getEntitlementList(url,header,source,cursor) responseBody = responseBody + JSON.parse( response.body ) cursor = response['Slpt-Cursor'] end puts "Total number of Entitlements of this source (ID #{source}): #{responseBody.count}" # Create a HashMap of name vs. ID named entitlementHash of all the existing entitlements of a source # These list will subsequently be used to get ID of an entitlement based on its name #entitlementList = responseBody['items'] # puts entitlementList responseBody.each {|a| entitlementHash["#{a['name'].upcase()}"] = a['id']} # puts entitlementHash puts "Entitlement Hash Map Creation Success!" return entitlementHash end def create_accessProfile(url,header,params) # This method is used to create an Access Profile using a hash Map of Parameters 'params' # Returns ID of the created or Access Profile if success else returns nil accessProId = nil # Create accessProfile header['Content-Type'] ='application/json' uri = URI.parse( "#{url}/v2/access-profiles" ) puts uri #uri = URI.parse( "#{url}/beta/access-profiles" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Post.new( uri.request_uri, header ) puts params.to_json request.body = params.to_json # request.set_form_data( params ) response = http.request( request ) json = JSON.parse( response.body ) puts response.body case response when Net::HTTPSuccess accessProId = json["id"] puts "Access Profile \'#{params['name']}\' creation is a Success!" # puts "Success!" when Net::HTTPUnauthorized puts "Access Profile \'#{params['name']}\' creation is a Failure!" puts "Error: Unauthorized." when Net::HTTPServerError puts "Access Profile \'#{params['name']}\' creation is a Failure!" puts "Error: Server Error." else puts "Access Profile \'#{params['name']}\' creation is a Failure" puts "Error: #{json['error']}" end return accessProId end def update_accessProfile(url,header,params,id) # This method is used to update an Access Profile using a hash Map of Parameters 'params' # Required parameters to be given in JSON format # {"name": "Name", "description": "Description", "sourceId": "12345", "entitlements": ["ID1", "ID2"], "id": "ID"} # Returns ID of the updated or Access Profile if success else returns nil accessProId = nil # Update accessProfile header['Content-Type'] ='application/json' uri = URI.parse( "#{url}/v2/access-profiles/#{id}" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Patch.new( uri.request_uri, header ) request.body = params.to_json response = http.request( request ) json = JSON.parse( response.body ) # puts response.body case response when Net::HTTPSuccess accessProId = json["id"] puts "Access Profile \'#{params['name']}\' update is a Success!" when Net::HTTPUnauthorized puts "Access Profile \'#{params['name']}\' update is a Failure!" puts "Error: Unauthorized." when Net::HTTPServerError puts "Access Profile \'#{params['name']}\' update is a Failure!" puts "Error: Server Error." else puts "Access Profile \'#{params['name']}\' update is a Failure" puts "Error: #{json['error']}" end return accessProId end def create_role(url,header,params) # This method is used to create a Role using a hash Map of Parameters 'params' # Returns ID of the created or Role if success else returns nil roleID = nil uri = URI.parse( "#{url}/cc/api/role/create" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Post.new( uri.request_uri, header ) request.set_form_data( params ) response = http.request( request ) json = JSON.parse( response.body ) # puts response.body case response when Net::HTTPSuccess # Catch roleID here and use it for update of Role below roleID = json["id"] puts "Role \'#{params['name']}\' creation is a Success!" when Net::HTTPUnauthorized puts "Role \'#{params['name']}\' creation is a Failure!" puts "Error: Unauthorized." when Net::HTTPServerError puts "Role \'#{params['name']}\' creation is a Failure!" puts "Error: Server Error." else puts "Role \'#{params['name']}\' creation is a Failure!" puts "Error: #{json['error']}" end return roleID end def update_role(url,header,params) # This method is used to update a Role using a hash Map of Parameters 'params' # Returns ID of the updated or Role if success else returns nil roleId = nil # Update Role header['Content-Type'] ='application/json' uri = URI.parse( "#{url}/cc/api/role/update/#{params['id']}" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Post.new( uri.request_uri, header ) request.body = params.to_json response = http.request( request ) json = JSON.parse( response.body ) # puts response.body case response when Net::HTTPSuccess roleId = json["id"] puts "Role \'#{params['displayName']}\' update is a Success!" # puts "Success!" when Net::HTTPUnauthorized puts "Role \'#{params['displayName']}\' update is a Failure!" puts "Error: Unauthorized." when Net::HTTPServerError puts "Role \'#{params['displayName']}\' update is a Failure!" puts "Error: Server Error." else puts "Role \'#{params['displayName']}\' update is a Failure" puts "Error: #{json['error']}" end return roleId end def delete_Roles(url,header,roleID) # This method delete a Role using its roleID # It returns true on success result = false header['Content-Type'] ='application/json' uri = URI.parse( "#{url}/cc/api/role/delete/#{roleID}" ) http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Post.new( uri.request_uri, header ) params = { "id"=> roleID } request.body = params.to_json response = http.request( request ) json = JSON.parse( response.body ) # puts json case response when Net::HTTPSuccess puts "Role deletion Success!" result = true when Net::HTTPServerError puts "Role deletion Failed!" puts "Authentication Failed: Server Error." else puts "Role deletion Failed!" puts "Error: #{json['error']}" end return result end def delete_AccessProfile(url,header,accessProfileID) # This method delete an Access Profile using its id # It returns true on success result = false header['Content-Type'] ='application/json' uri = URI.parse( "#{url}/v2/access-profiles/#{accessProfileID}" ) puts uri http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Delete.new( uri.request_uri, header ) params = { "id"=> accessProfileID } request.body = params.to_json response = http.request( request ) # json = JSON.parse( response.body ) # puts json case response when Net::HTTPSuccess puts "AccessProfile deletion Success!" result = true when Net::HTTPServerError puts "AccessProfile deletion Failed!" puts "Authentication Failed: Server Error." else puts "AccessProfile deletion Failed!" puts "Error: #{JSON.parse( response.body )}" end return result end def get_existingRole(url,header,roleId) # This method pulls details of an existing role role = nil uri = URI.parse( "#{url}/cc/api/role/get/?id=#{roleId}" ) # puts uri http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) # puts "Header: #{header}" case response when Net::HTTPSuccess role = JSON.parse( response.body ) # puts role puts "Role parameter pull for ID #{roleId} Success!" when Net::HTTPUnauthorized puts "Role parameter pull for ID #{roleId} Failed: Unauthorized." when Net::HTTPServerError puts "Role parameter pull for ID #{roleId} : Server Error." else puts "Role parameter pull for ID #{roleId} Failed. Response Code: #{response.code} " end return role end def get_identityID(url,header,idAlias) # This method pulls list of existing roles and create a hsh Map of displayName vs Id # Returns a hash Map id = nil # uri = URI.parse( "#{url}/v2/identities/#{idAlias}" ) # enAlias = URI.encode_www_form_component(idAlias, enc=Encoding::UTF_8) uri = URI.parse( "#{url}/v2/identities/#{idAlias}" ) # puts "Encoded URI: #{uri}" http = Net::HTTP.new( uri.host, uri.port ) http.use_ssl = true request = Net::HTTP::Get.new( uri.request_uri, header ) response = http.request( request ) case response when Net::HTTPSuccess # puts "Owner Alias: #{idAlias}" # Obtain a HashMap of the Identities, selector values will be used for membership if membership is not provided # for updating the existing role #puts response.body id = JSON.parse( response.body )["id"] # sourceList.each {|a| rolesHash["#{a['displayName']}"] = a['id']} # puts rolesHash puts "Identity parameter pull for Alias #{idAlias} Success!" when Net::HTTPUnauthorized puts "Identity parameter pull for Alias #{idAlias} Failed: Unauthorized." when Net::HTTPServerError puts "Identity parameter pull for Alias #{idAlias} : Server Error." else puts "Identity parameter pull for Alias #{idAlias} Failed" end return id end def replace_sourceNameWithsourceId(stringValue, replaceMap) # This method is used to replace value of sourceName with sourceId in the JASON string of # roleAssignmentValue as required is the ID of the source Object but given is the cloud Source Name replaceMap.each_key do |key| replaceStringKey='"sourceId":"'+key+'"' replaceStringValue='"sourceId":"'+replaceMap[key]+'"' stringValue.gsub!(replaceStringKey,replaceStringValue) end # puts stringValue return stringValue end #----------------------------------------------------------------------------------- # End of Methods #----------------------------------------------------------------------------------- #----------------------------------------------------------------------------------- # Start of Main Script #----------------------------------------------------------------------------------- puts "--------------------------------------------------------------" puts "IdentityNow Bulk Role/Access Profile Importer" puts "--------------------------------------------------------------" puts " Version: 8.0 " puts " Date: 2021-07-22" puts " Author: Rakesh Kapoor (rakesh.kapoor@sailpoint.com)" puts "--------------------------------------------------------------" configPath = File.dirname(__FILE__) # Authorization token expires by default in 3600 sec, give value less than 3600 for # variable tokenRenewalTime so that it renews token before expiring. # If token expires then script will fail. tokenRenewalTime = 3300 puts "Reading configuration...#{configPath}/config.json" puts "--------------------------------------------------------------" # put config.json file in following line. #config.json file should reside in same folder which holds this script data = JSON.parse( File.read( "#{configPath}/config.json" ) ) sourceNameVsId = Hash.new sourceNameVsSourceId = Hash.new sourceNameVsSourceIdRegular = Hash.new accessProfilesHash = Hash.new rolesHash = Hash.new unless data.nil? | data.empty? puts "Authenticating to IdentityNow..."; puts "--------------------------------------------------------------" # puts "URL: #{data['url']}"; # Creating header which will be used for all requests. # header = create_header(data['baseUrl'],data['user'],data['password'],data["apiUser"],data["apiKey"]) accessToken = data['accessToken'] if(accessToken.nil?) header = create_header(data['baseUrl'],data['clientId'],data['clientSecret']) else header = { "Authorization" => "Bearer #{accessToken}", "X-CSRF-Token" => "nocheck" } end unless header.nil? || header.empty? headerCreationTime = Time.new puts "Getting List of Sources" sourceList = generate_sourceVsIdLists(data['baseUrl'],header) sourceNameVsId = sourceList[0] sourceNameVsSourceId= sourceList[1] sourceNameVsSourceIdRegular = sourceList[2] puts "Getting List of Existing Access Profiles" accessProfilesHash = generate_accessProfileNameVsIdMap(data['baseUrl'],header) puts "Getting List of Existing Roles" rolesHash = generate_roleDisplayNameVsIdMap(data['baseUrl'],header) puts "Getting List of Governance Groups" groupsHash = generate_governanceGroupDisplayNameVsIDMap(data['baseUrl'], header) end # If no source exist no point in running this script unless sourceNameVsId.nil? || sourceNameVsId.empty? puts "Reading CSV file #{data['file']}..." puts "--------------------------------------------------------------" entitlementHash = Hash.new oldSource = "" # For avoiding "invalid byte sequence in UTF-8" error following encoding is added. # another encoding ':encoding => 'ISO-8859-1' can also be used. CSV.foreach("#{data['file']}",:encoding => 'windows-1251:utf-8') do |column| #puts column currentTime = Time.new # puts currentTime-headerCreationTime if(currentTime-headerCreationTime > tokenRenewalTime) # header = create_header(data['url'],data['user'],data['password'],data["apiUser"],data["apiKey"]) header = create_header(data['baseUrl'],data['clientId'],data['clientId'],data["apiUser"],data["apiKey"]) headerCreationTime = Time.new end unless header.nil? || header.empty? # puts column.inspect task = column[0] column_1 = column[1] column_2 = column[2] column_3 = column[3] column_4 = column[4] column_5 = column[5] column_6 = column[6] column_7 = column[7] column_8 = column[8] column_9 = column[9] column_10 = column[10] if(!column_1.nil?) column_1.strip! else column_1 = "" end if(!column_3.nil?) column_3.strip! else column_3 = "" end if(!column_5.nil?) column_5.strip! else column_5 = "" end if(!column_6.nil?) column_6.strip! else column_6 = "" end if(!column_7.nil?) column_7.strip! else column_7 = "" end if(!column_8.nil?) column_8.strip! else column_8 = "" end if(!column_9.nil?) column_9.strip! else column_9 = "" end if(!column_10.nil?) column_10.strip! else column_10 = "" end case task when "createAccessProfile" #-------------------------------------------------------------------------------------------------- # column order for task createAccessProfile in CSV file # createAccessProfile,accessProfileName,accessProfileDescription,source,entitlementList,owner,approvalSchemeList,deniedCommentsRequired,requestCommentsRequired,revokeRequestApprovalSchemes # entitlementList example entitlement1;entitlement2 #-------------------------------------------------------------------------------------------------- puts "Creating/Updating Access Profile" accessProfileSource = sourceNameVsId["#{column_3.upcase()}"] if(!accessProfileSource.nil?) entitlementIDArray = Array.new #----------------------------------------------------------- # Create a HashMap of displayableName vs. id of all the entitlements from obtained list of entitlements # This map will subsequently be used to get id of an entitlement based on its name # It compares with previous source name if it is same then it does not generate entitlement HashMap again # To save run time and resources it is always good idea to use CSV file sorted with source name if(oldSource != column_3) puts "Getting List of Entitlements for Source #{column_3}" entitlementHash = generate_entitlementDisplayNameVsIdMap(data['baseUrl'],header,accessProfileSource) oldSource = column_3 end #----------------------------------------------------------- if(!column_4.nil?) accessProfileEntitlements = column_4 accessProfileEntitlements = accessProfileEntitlements.split(';') #puts "accessProfileEntitlements: #{accessProfileEntitlements}" accessProfileEntitlements.each do |i| #i.strip! if(entitlementHash["#{i.upcase()}"].nil?) puts "Entitlement #{i} does not exists" else entitlementIDArray.push(entitlementHash["#{i.upcase()}"]) end end else puts "Entitlement column in CSV file is Empty" end # puts entitlementIDArray accessProfileID = accessProfilesHash[column_1.upcase()] #puts accessProfilesHash if accessProfileID.nil? # Access Profile is created here if it does not exist puts "Access Profile #{column_1} does not exist and will be created" # puts column_5 owner = get_identityID(data['baseUrl'],header,column_5) params = { "name" => column_1, "description" => column_2, "sourceId" => sourceNameVsSourceId[column_3.upcase()], "entitlements" => entitlementIDArray } # puts "owner: #{owner}:" if !column_5.nil? && column_5 != "" owner = get_identityID(data['baseUrl'],header,column_5) params["ownerId"] = owner else params["ownerId"] = "" end if !column_6.nil? && column_6 != "" # Begin logic to parse governance groups, if they exist in the file. puts "Beginning Split of approval scheme column. Looking for governance group." approvalSchemeArray = column_6.split(',') # puts "Approvers: #{approvalSchemeArray}" approvalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_6 = approvalSchemeArray.join(",") params["approvalSchemes"] = column_6 else params["approvalSchemes"] = "" end if !column_7.nil? && column_7 != "" && (column_7.upcase() == "TRUE" || column_7.upcase() == "FALSE") params["deniedCommentsRequired"] = column_7.downcase() end if !column_8.nil? && column_8 != "" && (column_8.upcase() == "TRUE" || column_8.upcase() == "FALSE") params[ "requestCommentsRequired"] = column_8.downcase() end if !column_9.nil? && column_9 != "" # Begin logic to parse governance groups for revoke request approvers, if they exist in the file. puts "Beginning Split of revoke request approval schemes column. Looking for governance group." revokeApprovalSchemeArray = column_9.split(',') # puts "Revoke Request Approvers: #{revokeApprovalSchemeArray}" revokeApprovalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Revoke Request Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_9 = revokeApprovalSchemeArray.join(",") params["revokeRequestApprovalSchemes"] = column_9 else params["revokeRequestApprovalSchemes"] = "" end #puts "Params: #{params}" accessProfileID = create_accessProfile(data['baseUrl'],header,params) if accessProfileID.nil? accessProfilesHash[column_1.upcase()] = accessProfileID end else # Access Profile is updated here if it exists puts "Access Profile #{column_1} already exist and will be updated" params = { "name" => column_1, "description" => column_2, "sourceId" => sourceNameVsSourceId[column_3.upcase()], "entitlements" => entitlementIDArray } if !column_5.nil? && column_5 != "" owner = get_identityID(data['baseUrl'],header,column_5) params["ownerId"] = owner end if !column_6.nil? && column_6 != "" # Begin logic to parse governance groups, if they exist in the file. puts "Beginning Split of approval scheme column. Looking for governance group." approvalSchemeArray = column_6.split(',') # puts "Approvers: #{approvalSchemeArray}" approvalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_6 = approvalSchemeArray.join(",") params["approvalSchemes"] = column_6 else params["approvalSchemes"] = "" end if !column_7.nil? && column_7 != "" if column_7.upcase() == "TRUE" params["deniedCommentsRequired"] = true else params["deniedCommentsRequired"] = false end end if !column_8.nil? && column_8 != "" if column_8.upcase() == "TRUE" params[ "requestCommentsRequired"] = true else params[ "requestCommentsRequired"] = false end end if !column_9.nil? && column_9 != "" # Begin logic to parse governance groups for revoke request approvers, if they exist in the file. puts "Beginning Split of revoke request approval schemes column. Looking for governance group." revokeApprovalSchemeArray = column_9.split(',') # puts "Revoke Request Approvers: #{revokeApprovalSchemeArray}" revokeApprovalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Revoke Request Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_9 = revokeApprovalSchemeArray.join(",") params["revokeRequestApprovalSchemes"] = column_9 else params["revokeRequestApprovalSchemes"] = "" end #, # "id"=> accessProfileID # puts "Params: #{params}" accessProfileID = update_accessProfile(data['baseUrl'],header,params,accessProfileID) end if accessProfileID.nil? puts "Access Profile #{column_1} neither exist nor could be created" end else puts "Source name #{column_3} does not exist, skipping Access Profile creation" end when "createRole" #-------------------------------------------------------------------------------------------------- # column order for task createRole in CSV file # createRole,roleName,roleDescription,roleDisabled (true/false),roleOwner,accessProfileList,requestable,approvalSchemes,deniedCommentsRequired,requestCommentsRequired,revokeRequestApprovalSchemes # accessProfileList example accessProfile1;accessProfile2 #-------------------------------------------------------------------------------------------------- puts "Creating/Updating Role" accessProfileIDArray = Array.new accessProfileIDs = column_5 accessProfileIDs = accessProfileIDs.split(';') accessProfileIDs.each do |i| if(accessProfilesHash["#{i.upcase()}"].nil?) puts "Access Profile #{i} does not exists" else accessProfileIDArray.push(accessProfilesHash["#{i.upcase()}"]) end end roleID = rolesHash[column_1.upcase()] #If Role does not exist it has to be created. if roleID.nil? puts "Role \'#{column_1}\' does not Exist, getting created" params = { "name" => column_1, "description" => column_2, "disabled" => column_3, "owner" => column_4, "accessProfileIds"=> accessProfileIDArray } if !column_6.nil? && column_6 != "" if column_6.upcase() == "TRUE" params["requestable"] = true if !column_7.nil? && column_7 != "" # Begin logic to parse governance groups for approvers, if they exist in the file. puts "Beginning Split of approval scheme column. Looking for governance group." approvalSchemeArray = column_7.split(',') approvalSchemeArray.each { |item| if(item != "manager" && item != "owner") # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_7 = approvalSchemeArray.join(",") params["approvalSchemes"] = column_7 else params["approvalSchemes"] = "none" end if !column_8.nil? && column_8 != "" if column_8.upcase() == "TRUE" params["deniedCommentsRequired"] = true else params["deniedCommentsRequired"] = false end end if !column_9.nil? && column_9 != "" if column_9.upcase() == "TRUE" params[ "requestCommentsRequired"] = true else params[ "requestCommentsRequired"] = false end end if !column_10.nil? && column_10 != "" # Begin logic to parse governance groups for revoke request approvers, if they exist in the file. puts "Beginning Split of revoke request approval schemes column. Looking for governance group." revokeApprovalSchemeArray = column_10.split(',') # puts "Revoke Request Approvers: #{revokeApprovalSchemeArray}" revokeApprovalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Revoke Request Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_10 = revokeApprovalSchemeArray.join(",") params["revokeRequestApprovalSchemes"] = column_10 else params["revokeRequestApprovalSchemes"] = "" end else params["requestable"] = false end end roleID = create_role(data['baseUrl'],header,params) if !roleID.nil? rolesHash[column_1.upcase()] = roleID end else # Role is updated here once accessProfile ID is known puts "Role \'#{column_1}\' already exists, getting Updated" roleMap = get_existingRole(data['baseUrl'],create_header(data['baseUrl'],data['clientId'],data['clientSecret']),roleID) roleType = nil if !roleMap.nil? roleType = roleMap['selector']['type'] if !roleType.nil? && roleType != "" params = { "id"=> roleID, "displayName"=> column_1, "description"=> column_2, "disabled"=> column_3, "owner"=> column_4, "accessProfileIds"=> accessProfileIDArray, "selector" => roleMap['selector'] } # puts params else params = { "id"=> roleID, "displayName"=> column_1, "description"=> column_2, "disabled"=> column_3, "owner"=> column_4, "accessProfileIds"=> accessProfileIDArray, } end else params = { "id"=> roleID, "displayName"=> column_1, "description"=> column_2, "disabled"=> column_3, "owner"=> column_4, "accessProfileIds"=> accessProfileIDArray, } end if !column_6.nil? && column_6 != "" if column_6.upcase() == "TRUE" params["requestable"] = true if !column_7.nil? && column_7 != "" # Begin logic to parse governance groups, if they exist in the file. puts "Beginning Split of approval scheme column. Looking for governance group." approvalSchemeArray = column_7.split(',') approvalSchemeArray.each { |item| if(item != "manager" && item != "owner") # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_7 = approvalSchemeArray.join(",") params["approvalSchemes"] = column_7 else params["approvalSchemes"] = "none" end if !column_8.nil? && column_8 != "" if column_8.upcase() == "TRUE" params["deniedCommentsRequired"] = true else params["deniedCommentsRequired"] = false end end if !column_9.nil? && column_9 != "" if column_9.upcase() == "TRUE" params[ "requestCommentsRequired"] = true else params[ "requestCommentsRequired"] = false end end if !column_10.nil? && column_10 != "" # Begin logic to parse governance groups for revoke request approvers, if they exist in the file. puts "Beginning Split of revoke request approval schemes column. Looking for governance group." revokeApprovalSchemeArray = column_10.split(',') # puts "Revoke Request Approvers: #{revokeApprovalSchemeArray}" revokeApprovalSchemeArray.each { |item| if(item != "manager" && item != "accessProfileOwner" && item != "sourceOwner" && item != "appOwner") # puts "Revoke Request Approver: #{item}" # do lookup of governance group governanceGroupId = groupsHash["#{item}"] itemModified = "workgroup: #{governanceGroupId}" item.replace(itemModified) end } #rejoin the strings column_10 = revokeApprovalSchemeArray.join(",") params["revokeRequestApprovalSchemes"] = column_10 else params["revokeRequestApprovalSchemes"] = "" end else params["requestable"] = false end end roleID = update_role(data['baseUrl'],header,params) end if roleID.nil? #When role fails to be created puts "Role \'#{column_1}\' neither exist nor could be created" end when "assignUsersToRole" #-------------------------------------------------------------------------------------------------- # column order for task assignUsersToRole in CSV file # assignUserToRole,roleName,roleAssignmentType,roleAssignmentValue # If roleAssignmentType is VALUE_MAP, roleAssignmentValue should be in JSON format as below # [{ ""operation"": ""EQ"", ""property"": ""department"", ""value"": ""myDepartment"" }] #-------------------------------------------------------------------------------------------------- puts "Assigning Role \'#{column_1}\' to users by updating it" # puts JSON.parse(roleAssignmentValue) # If Role exist it will be assigned to Users based on roeAssignmentValue from 4th column roleID = rolesHash[column_1.upcase()] aliasList = [] valueMap = [] complexValue = {} if column_2.strip .eql? "IDENTITY_LIST" aliasList = JSON.parse(column_3) end if column_2.strip.eql? "VALUE_MAP" valueMap = JSON.parse(column_3) end if column_2.strip .eql? "COMPLEX_CRITERIA" puts "Replacing source Name with sourceId in roleAssignValue String" column_3 = replace_sourceNameWithsourceId(column_3,sourceNameVsSourceIdRegular) complexValue = JSON.parse(column_3) # puts complexValue end if !roleID.nil? params = { "id"=> roleID, "displayName"=> column_1, "selector"=> { "type"=> column_2.strip, "complexRoleCriterion"=>complexValue, "valueMap"=> valueMap, "aliasList" => aliasList } } roleID = update_role(data['baseUrl'],header,params) else puts "Role \'#{column_1}\' does not exist" end when "deleteRoles" #-------------------------------------------------------------------------------------------------- # column order for task deleteRoles in CSV file # deleteRoles,roleNameList # roleNameList example role1;role2 #-------------------------------------------------------------------------------------------------- puts "Deleting Roles" roleIDs = column_1 roleIDs = roleIDs.split(';') roleIDs.each do |i| roleID = rolesHash["#{i.upcase()}"] if(roleID.nil?) puts "Role #{i} does not exists" else puts "Deleting Role \'#{i}\'" if( delete_Roles(data['baseUrl'],header,roleID)) rolesHash.delete("#{i.upcase()}") end end end when "deleteAccessProfiles" #-------------------------------------------------------------------------------------------------- # column order for task deleteAccessProfiles in CSV file # deleteAccessProfiles,accessProfileNameList # accessProfileNameList example accessProfile1;accessProfile2 #-------------------------------------------------------------------------------------------------- puts "Deleting Access Profiles" accessPIDs = column_1 accessPIDs = accessPIDs.split(';') accessPIDs.each do |i| accessProfileID = accessProfilesHash["#{i.upcase()}"] if(accessProfileID.nil?) puts "Access Profile #{i} does not exists" else puts "Deleting Access Profile \'#{i}\'" if(delete_AccessProfile(data['baseUrl'],header,accessProfileID)) accessProfilesHash.delete("#{i.upcase()}") end end end when "" puts "No task is defined in first column, its empty" else puts "Wrong task name: \'#{task}\' in first column, valid task names are: \n createRole, createAccessProfile, assignUsersToRole, deleteRoles and deleteAccessProfiles" end end end end end