附註:
- 此教學課程需要存取 Oracle Cloud。若要註冊免費帳戶,請參閱開始使用 Oracle Cloud Infrastructure Free Tier 。
- 它使用 Oracle Cloud Infrastructure 憑證、租用戶及區間的範例值。完成實驗室時,請以雲端環境特有的值取代這些值。
使用 Python 從以 UserStatus 和 LastSuccessfulLoginDate 為基礎的 OCI IAM 識別網域刪除非作用中使用者
簡介
Oracle Cloud Infrastructure (OCI) 是一個能夠提供一系列雲端服務的雲端平台,包括儲存、網路及基礎架構。Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) 是一項服務,可讓您管理對 OCI 資源的存取。它為使用者和群組提供認證和授權。
根據非作用中使用者在 OCI IAM Identity Domains 的上次成功登入日期刪除非作用中使用者,在 OCI 的每位使用者定價模型內容中特別有幫助,原因如下:
-
成本最佳化: OCI 採用每位使用者定價模型,這表示您將對每個使用者帳戶計費。透過定期識別和刪除非作用中使用者,您可以大幅降低訂閱成本。隨著組織的規模和使用者人數增加,成本最佳化變得更加重要。
-
安全性增強功能:非作用中使用者帳戶有潛在的安全性風險。這些帳戶可能不再由合法使用者使用,但如果不管如何,惡意動作者仍可利用這些帳戶。刪除非作用中使用者可確保只有經過授權的個人才能夠存取您的 OCI 資源,從而減少受攻擊面並增強整體安全性。
-
Resource Management:管理大量非作用中的使用者帳戶可能會受到管理上的麻煩。它會凌亂您的 IAM 環境,使其更難專注於作用中使用者帳戶。自動化識別和刪除非作用中使用者的程序釋放管理資源,並讓您的團隊專注於更具策略性的任務。
-
規範:規範標準通常要求組織定期複查和管理使用者帳戶。刪除非作用中使用者可協助您符合規範需求,並示範對資料安全性和治理的承諾。
若要有效實作此程序,可以在 python 程式碼片段下方,根據非作用中使用者的上次成功登入日期和作用中 / 非作用中狀態,自動識別和刪除非作用中使用者。透過定期執行這類命令檔,您可以維護精實而安全的 IAM 環境,同時最佳化 OCI 中的成本。
適用對象
IAM 專業人員和管理員。
目標
使用搭配 Python 的 REST API,根據使用者狀態和 OCI IAM 識別網域中的 lastSuccessfulLoginDate 刪除使用者。
必要條件
-
作用中的 OCI 訂閱。
-
熟悉 OCI IAM 和 Python。
-
必須具備使用 OCI IAM Identity Domains REST API 的知識。
-
具備管理應用程式授權的 IAM 使用者 (識別網域管理員、安全管理員或應用程式管理員)。
-
您的系統已安裝 Python 3.x。
-
已安裝
urllib3
、requests
及datetime
個 Python 套裝軟體。
作業 1:在 OCI IAM 識別網域中建立機密應用程式
請依照 Oracle Identity Cloud Service:第一個 REST API 呼叫建立機密應用程式並擷取從屬端 ID 和從屬端加密密碼,然後用來對 OCI IAM 執行 REST API 呼叫以擷取存取權杖和後續 API 端點。
工作 2:設定 config.json
檔案
在本機機器上設定組態檔。config.json
檔案包含用於產生存取權杖之識別網域 URL、從屬端 ID 和從屬端加密密碼的相關資訊。
{
"iamurl" : "https://idcs-###########.identity.oraclecloud.com",
"client_id" : "#######################",
"client_secret" : "#######################"
}
工作 3:取得存取權杖
config.json
檔案就緒之後,您首先需要產生存取權杖,可用來進一步呼叫 OCI IAM 端點的 REST API。
在以下程式碼片段中,函數 get_encoded 使用「從屬端 ID」和「從屬端加密密碼」作為引數,並傳回 base64-encoded
字串。此編碼的字串會進一步以引數形式傳送給函數 get_access_token
作為「授權」標頭,以執行 POST 要求來取得存取權杖。
#get base64 encoded
def get_encoded(self,clid, clsecret):
encoded = clid + ":" + clsecret
baseencoded = base64.urlsafe_b64encode(encoded.encode('UTF-8')).decode('ascii')
return baseencoded
#get access token
def get_access_token(self,url, header):
para = "grant_type=client_credentials&scope=urn:opc:idm:__myscopes__"
response = requests.post(url, headers=header, data=para, verify=False)
jsonresp = json.loads(response.content)
access_token = jsonresp.get('access_token')
return access_token
#print access token
def printaccesstoken(self):
obj = IAM()
encodedtoken = obj.get_encoded(clientID, clientSecret)
extra = "/oauth2/v1/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
'Authorization': 'Basic %s' % encodedtoken, 'Accept': '*/*'}
accesstoken = obj.get_access_token(idcsURL + extra, headers)
return accesstoken
工作 4:處理一批使用者資訊 (lastSuccessfulLoginDate、userStatus) 以清除使用者
我們可以使用此存取權杖對不同的 OCI IAM 識別網域 REST 端點進行進一步的 REST API 呼叫。下方的程式碼片段顯示我們如何對 /admin/v1/Users
端點提出必要標頭和參數的 GET 要求,以便擷取使用者總數,然後根據結果總數和每個要求的計數計算所需的重複次數。
-
此函數會取得 YYYY-MM-DD 格式的目前日期,並儲存在變數
current_date
中,然後重複各個使用者資料。 -
在每個反覆測試中,它會傳送具有適當參數的 GET 要求,以擷取一批使用者資訊 (使用者名稱、lastSuccessfulLoginDate 及作用中) ,然後執行一些條件檢查。
-
然後,程式碼會根據定義的條件檢查,分別針對
username.txt
和userid.txt
檔案中的所有相符使用者附加使用者名稱和使用者 ID,並將它標示為要刪除。首先,它會檢查使用者狀態,如果是 False (意指 inactive),它會將使用者附加至txt
檔案,並繼續下一個循環。它接著會檢查使用者狀態,如果是無,則會列印從未存取的 _{user}。決定在主控台上手動刪除,並繼續下一個循環。 -
此命令檔會擷取使用者的上次成功登入日期、計算自上次登入後的天數,然後列印到主控台。對於任何未登入超過 90 天的使用者,命令檔會繼續檢查使用者是否有相關的特定管理員角色。所有未登入 90 天且具有無管理員角色的使用者都會被識別並標示為要刪除。
-
處理所有使用者之後,要刪除的一組使用者 ID 會寫入 JSON 檔案
username.json
。大量刪除要求是透過格式化特定 JSON 格式的資料來準備。大量刪除要求接著會傳送到/admin/v1/Bulk
端點,並會列印一則訊息,指出已刪除識別的使用者。 -
在所有反覆運算之後,函數會返回主函數。
注意:有一個程式碼段落加上說明,說明未來動作或手動決策,例如處理從未存取系統的使用者。有可能是管理員可能沒有存取 OCI IAM 且您可能不想要刪除的情境。
def get_successfullogindate(self): extra = "/admin/v1/Users" obj = IAM() accesstoken = obj.printaccesstoken() headers = {'Authorization': 'Bearer ' + accesstoken} resp = requests.get(idcsURL+extra, headers=headers, verify=False) jsonresp = json.loads(resp.content) totalCount = jsonresp.get("totalResults") print("Total number of users: " + str(totalCount)) current_date = datetime.now().date() startIndex = 1 count = 50 loop = int(totalCount / count) extra2 = "/admin/v1/AppRoles" for i in range(loop + 1): param = { 'attributes': "username,active,urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User:lastSuccessfulLoginDate", 'startIndex': startIndex, 'count': count} resp = requests.get(idcsURL+extra, headers=headers, verify=False, params=param) startIndex += count jsonresp = json.loads(resp.content) tempjsn = jsonresp.get("Resources") for x in tempjsn: trimjsn = {} username = trimjsn['userName'] = x.get("userName") userid = x.get("id") userStatus = x.get("active") userState = x.get("urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User") if userStatus is False: print(username + " is in inactive state. Will be deleted.") with open('username.txt', 'a') as c: c.write(username) c.write('\n') with open('userID.txt', 'a') as d: d.write(userid) d.write('\n') continue if userState is None: print(username + " has never accessed. Kindly make a decision to delete it manually on console") # with open('username.txt', 'a') as c: # c.write(username) # c.write('\n') # with open('userID.txt', 'a') as d: # d.write(userid) # d.write('\n') # ** Uncomment the lines commented above to delete the users who have never accessed ** continue lastLoginDate = x['urn:ietf:params:scim:schemas:oracle:idcs:extension:userState:User']['lastSuccessfulLoginDate'] target_dates = datetime.strptime(lastLoginDate, '%Y-%m-%dT%H:%M:%S.%fZ').date() num_days = (current_date - target_dates).days #print(username + " Last logged in " + str(num_days) + " days ago") if num_days > 30: #print("entered if condition for more than 90 days ") params2 = {"filter": f'members[value eq "{userid}"] and app.value eq "IDCSAppId" and (displayName eq "Identity Domain Administrator" or displayName eq "Security Administrator" or displayName eq "Application Administrator" or displayName eq "User Administrator" or displayName eq "User Manager" or displayName eq "Help Desk Administrator" or displayName eq "Audit Administrator")'} resp2 = requests.get(idcsURL+extra2, headers=headers, verify=False, params=params2) jsonresp3 = json.loads(resp2.content) totalCount1 = jsonresp3.get("totalResults") if totalCount1 < 1: print(username + " has not Logged in for 90 days and has NO admin role. Will be deleted.") with open('username.txt', 'a') as c: c.write(username) c.write('\n') with open('userID.txt', 'a') as d: d.write(userid) d.write('\n') try: with open('userID.txt','r') as g: lines = len(g.readlines()) except FileNotFoundError: print("No users found to be Deleted by the Script") exit() x=0 while x<lines: with open('userID.txt','r') as e: content = e.readlines() id=content[x] id=id.rstrip(id[-1]) paradelete=json.dumps( { "method": "DELETE", "path": "/Users/"+id+"?forceDelete=true" } ) with open('username.json', 'a') as j: j.writelines(paradelete) j.write(',') x=x+1 with open('username.json', 'r') as file: data = file.read() with open('formatedData.json', 'a') as file1: file1.writelines('{ \n') file1.writelines('"schemas": ["urn:ietf:params:scim:api:messages:2.0:BulkRequest"],\n') file1.writelines('"Operations": [\n') data2=data.rstrip(data[-1]) file1.write(data2) file1.write(']}') headers = {'Authorization': 'Bearer ' + accesstoken} bulkdata = json.load(open('formatedData.json')) param = {'forceDelete': True} payload = json.dumps(bulkdata) headers2 = {'Content-Type': 'application/json','Authorization': 'Bearer ' + accesstoken, 'Accept': '*/*'} extra2="/admin/v1/Bulk" #respdelete = requests.request("POST",idcsURL + extra2, headers=headers2, verify=False, params=param,data=payload) print("The identified users has been deleted ") try: os.remove('userID.txt') os.remove('username.txt') os.remove('formatedData.json') os.remove('username.json') except FileNotFoundError: exit()
作業 5:使用 OCI Cloud Shell 中的命令檔
命令檔就緒之後,就可以在本機機器 (已安裝 Python) 或任何支援 Python 開發的 IDE 上輕鬆執行。我們使用 OCI Cloud Shell 執行命令檔並取得想要的報表。
-
登入 OCI 主控台,從畫面右上角開啟 Cloud Shell,然後上傳 Python 命令檔和
config.json
檔案。 -
執行
python DeleteUser_PriorCheck_InactivePeriod60days_IsAdmin.py
。注意:
DeleteUser_PriorCheck_InactivePeriod60days_IsAdmin.py
是使用上述程式碼片段開發的 Python 命令檔。
相關連結
認可
-
作者 - Gautam Mishra (資深雲端工程師)
-
貢獻者 - Chetan Soni (雲端解決方案工程師)
其他學習資源
瀏覽 docs.oracle.com/learn 的其他實驗室,或前往 Oracle Learning YouTube 頻道存取更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。
如需產品文件,請造訪 Oracle Help Center 。
Delete Inactive Users from OCI IAM Identity Domains Based on UserStatus and LastSuccessfulLoginDate Using Python
F89514-01
November 2023
Copyright © 2023, Oracle and/or its affiliates.