References RestSharp.dll References NewtonSoft.Json.dll Imports System.Security.Cryptography.X509Certificates Imports NewtonSoft.Json.Linq Imports RestSharp ' DO NOT USE THAT IN PRODUCTION ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!! ' Delegate to allow all SSL certificates (Including self-signed) System.Net.ServicePointManager.ServerCertificateValidationCallback = Function(se As Object, cert As X509Certificate, chain As X509Chain, sslerror As System.Net.Security.SslPolicyErrors) True Dim pwRetrievalSuccesfull As Boolean = False Dim pw As String = String.Empty Dim requestID As String = String.Empty Dim accountID As String = String.Empty Dim accessToken As String = String.Empty Dim response As IRestResponse Dim responseArray As JArray Dim responseObj As JObject Dim body As JObject Dim pwWithErrorStatus As KeyValuePair(Of String, Boolean) Dim certCol As New X509CertificateCollection() ' Getting the account name ' - for which the password Is fetched from SafeGuard - ' from another variable in the "currently used" variable set Dim accountName As String = VariableSet("CP_d1imloginaccount", "") ' Set the SafeGuard URI configurable by ConfigParm Dim SafeGuardUrl As String = args.QueryDatabase(Connection.SystemQuery.From("DialogConfigParm").Select("Value").Filter("Fullpath='Custom\SafeGuard\URL'")).Result.First.GetValue("Value").AsString ' Set the thumbprint for the Client Credentials used to connect to SafeGuard configurable by ConfigParm Dim thumbprint As String = args.QueryDatabase(Connection.SystemQuery.From("DialogConfigParm").Select("Value").Filter("Fullpath='Custom\SafeGuard\ThumbPrint'")).Result.First.GetValue("Value").AsString ' Fetch the certificate from the local CERT store used to access SafeGuard Dim store As X509Store = New X509Store(StoreName.My, StoreLocation.CurrentUser) store.Open(OpenFlags.ReadOnly) ' Assign the certificate to the RestSharp Client certCol = New X509CertificateCollection(store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, False)) store.Close() ' Create the RestSharp Client to access SafeGuard Dim client As RestClient = New RestClient(SafeGuardUrl) With {.CookieContainer = New Net.CookieContainer(), .ClientCertificates = certCol} ' Define functions für web requests Dim GetAccessToken = Function() As String ' Get the access token from SafeGuard using client credentials (certificate) Dim _request = New RestRequest("RSTS/oauth2/token", Method.POST) With {.RequestFormat = DataFormat.Json} '_request.AddHeader("Content-Type", "application/json") body = New JObject() From {{"grant_type", "client_credentials"}, {"scope", "dell:sts:primaryproviderid:certificate"}} _request.AddParameter("application/json", body.ToStringNullSafe(), RestSharp.ParameterType.RequestBody) response = client.Execute(_request) ' Check if request for the access token was successfull If response.StatusCode = Net.HttpStatusCode.OK Then ' Extract Access Token responseObj = JObject.Parse(response.Content) accessToken = responseObj.SelectToken("access_token").ToStringNullSafe() Return accessToken Else Return String.Empty End If End Function Dim FetchAccountID = Function(ByVal _AccountName As String) As String ' Fetch id for account from SafeGuard Dim _request As RestRequest = New RestRequest("service/core/v1/Me/RequestableAccounts", Method.GET) With {.RequestFormat = DataFormat.Json} _request.AddParameter("Authorization", String.Format("Bearer {0}", accessToken), RestSharp.ParameterType.HttpHeader) ' Add filter for accountname ' Simple AD Specific Filter for the account name splitting by first \ Dim _accountInfo() As String = Split(_AccountName, "\", 2) Select Case _accountInfo.Count() Case 1 _request.AddQueryParameter("filter", String.Format("Name eq '{0}'", _accountInfo(0))) Case 2 _request.AddQueryParameter("filter", String.Format("Name eq '{0}' and (NetBiosName eq '{1}' or DomainName eq '{1}')", _accountInfo(1), _accountInfo(0))) Case Else _request.AddQueryParameter("filter", String.Format("Name eq '{0}'", _AccountName)) End Select response = client.Execute(_request) ' Check if request for account to retrieve password for was successfull If response.StatusCode = Net.HttpStatusCode.OK Then ' Extract id from response responseArray = JArray.Parse(response.Content) ' Check if only one account was retrieved If responseArray.Count = 1 Then Return responseArray.SelectToken("[0].Id").ToStringNullSafe() End If End If Return String.Empty End Function Dim CreateNewPasswordRequest = Function(ByVal _accountID As String) As String ' Create new password request Dim _request As RestRequest = New RestRequest("service/core/v1/PasswordRequests", Method.POST) With {.RequestFormat = DataFormat.Json} _request.AddParameter("Authorization", String.Format("Bearer {0}", accessToken), RestSharp.ParameterType.HttpHeader) ' Create body for password request body = New JObject() From { {"AccountId", _accountID}, {"IsEmergency", True}, {"ReasonCodeId", ""}, {"ReasonComment", "One Identity Manager Sync Password Checkout"}, {"RequestedDurationDays", ""}, {"RequestedDurationHours", ""}, {"RequestedDurationMinutes", ""}, {"RequestedFor", ""}, {"TicketNumber", ""} } _request.AddParameter("application/json", body.ToStringNullSafe(), RestSharp.ParameterType.RequestBody) response = client.Execute(_request) ' Check if request was successfully created If response.StatusCode = Net.HttpStatusCode.Created Then responseObj = JObject.Parse(response.Content) ' Extract id from response Return responseObj.SelectToken("Id").ToStringNullSafe() Else Throw New Exception(response.Content) End If End Function Dim CheckoutPasswordForRequestID = Function(ByVal _requestID As String) As KeyValuePair(Of String, Boolean) ' Checkout password for request ID Dim _request As RestRequest = New RestRequest("service/core/v1/PasswordRequests/{passwordRequestId}/CheckOutPassword", Method.POST) With {.RequestFormat = DataFormat.Json} _request.AddParameter("Authorization", String.Format("Bearer {0}", accessToken), RestSharp.ParameterType.HttpHeader) _request.AddUrlSegment("passwordRequestId", _requestID) response = client.Execute(_request) ' Check if password could be fetched Select Case response.StatusCode Case Net.HttpStatusCode.OK Return New KeyValuePair(Of String, Boolean)(JToken.Parse(response.Content).ToStringNullSafe(), False) Case Net.HttpStatusCode.BadRequest ' Check if request has been expired or if the password has been checked-in in the meantime as reson for BadRequest If JToken.Parse(response.Content)("Code").ToStringNullSafe() = "90405" Then Return New KeyValuePair(Of String, Boolean)(String.Empty, True) Else ' Throw exception in all other cases Throw New Exception(response.Content) End If Case Else Throw New Exception(response.Content) End Select End Function Dim AcknowledgePasswordRequest = Function(ByVal _requestID As String) As Boolean ' Acknowledge password request Dim _request As RestRequest = New RestRequest("service/core/v1/PasswordRequests/{passwordRequestId}/Acknowledge", Method.POST) With {.RequestFormat = DataFormat.Json} _request.AddParameter("Authorization", String.Format("Bearer {0}", accessToken), RestSharp.ParameterType.HttpHeader) _request.AddUrlSegment("passwordRequestId", _requestID) response = client.Execute(_request) ' Check if request could be acknowledged If response.StatusCode = Net.HttpStatusCode.OK Then Return True Else Throw New Exception(response.Content) End If End Function Dim FetchExistingValidRequest = Function(ByVal _accountId As String) As String ' Check if password request for this account already exists Dim _request As RestRequest = New RestRequest("service/core/v1/PasswordRequests", Method.GET) With {.RequestFormat = DataFormat.Json} _request.AddParameter("Authorization", String.Format("Bearer {0}", accessToken), RestSharp.ParameterType.HttpHeader) Try response = client.Execute(_request) ' Check if list of current requests could be fetched If response.StatusCode = Net.HttpStatusCode.OK Then responseArray = JArray.Parse(response.Content) responseObj = responseArray.Children(Of JObject)().FirstOrDefault(Function(o) o("AccountId").ToString() = _accountId) ' Check if password request could be extracted from response If Not responseObj Is Nothing Then ' Check if request needs to be acknowledged (because of expiration or revokation) If responseObj.SelectToken("NeedsAcknowledgement").ToObject(Of Boolean) Then AcknowledgePasswordRequest(responseObj.SelectToken("Id").ToStringNullSafe()) Return String.Empty Else Return responseObj.SelectToken("Id").ToStringNullSafe() End If Else Return String.Empty End If Else Throw New Exception(response.Content) End If Catch e As Exception Throw New Exception(e.Message) End Try End Function ' Get the access token from SafeGuard using client credentials (certificate) accessToken = GetAccessToken() ' Check if access token could be retrieved If Not String.IsNullOrEmpty(accessToken) Then ' Fetch accountID for accountName from SafeGuard accountID = FetchAccountID(accountName) ' Check if accountID could be fetched If Not String.IsNullOrEmpty(accountID) Then Dim intRetries As Integer = 3 Do Try ' Fetch existing accounts from same user for the accountID requestID = FetchExistingValidRequest(accountID) ' If no request exists If String.IsNullOrEmpty(requestID) Then ' Create new password request for account requestID = CreateNewPasswordRequest(accountID) End If ' Checkout password for request ID pwWithErrorStatus = CheckoutPasswordForRequestID(requestID) ' Check if password could be fetched If Not pwWithErrorStatus.Value Then pw = pwWithErrorStatus.Key ' DO NOT UNCOMMENT THE NEXT LINE IN PRODUCTION ' !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 'Log.Debug("Sync Password from SafeGuard:{0}", pw) pwRetrievalSuccesfull = True End If Catch e As Exception Log.Debug(e.Message) End Try intRetries = intRetries - 1 Loop Until pwRetrievalSuccesfull = True Or intRetries = 0 End If End If If pwRetrievalSuccesfull Then Return pw Else Throw New Exception("Password could not be fetched from password vault.") End If