Hi,
I'm writing a program which extract informations from PW datasource, using the Web Services Gateway.
We use Bentley IMS to connect to datasource, so I want to use the same method. The documentation mention Bentley STS and OAuth2, but is not very precise and I don't find a way to achieve that :
All requests having a {RepositoryId} parameter must have one of the following:
Does anyone can explain how to configure the header using the token, or give an example ?
Thanks
Benjamin
I've just been doing some work in this area and it was pointed out that there has been no answer to this.Here's a very simple PowerShell script using WSG to download files. You will need at least version 1.11.2.0 of PWPS_DAB to run this script.
$wsgURL = 'decide-pwce-us-ws.bentley.com/.../Bentley.PW--'$dsn = 'decide-pwce-us.bentley.com~3Adecide-pwce-us-10'$class = 'PW_WSG/Document'$id = '560ff1f5-bcab-4527-916b-6d240e0c45f8'
# $downloadUrl = 'decide-pwce-us-ws.bentley.com/.../$file'
$downloadUrl = "$wsgURL$dsn/$class/$id/" + '$file'
# requires federated account$token2 = Get-PWConnectionClientToken -ConnectedProjectUser dave.brumbaugh@eagle.bentley.com -ConnectedProjectPassword (Read-Host -Prompt Password -AsSecureString)
# just for information to see the underlying SAMLConvertFrom-EncodedToken $token2
$random = Get-RandomString -Length 10 -Characters "abcdefghijklmnopqrstuvwxyz"
Invoke-WebRequest -Method Get -Uri $downloadUrl -Headers @{Authorization = 'Token ' + $token2} -OutFile ("c:\temp\" + ($random) + ".pdf")
# alternative method for connecting to WSG with logical user account$logicalToken = Get-EncodedLogicalToken -User "MyUser" -Password (Read-Host -Prompt Password -AsSecureString)
Invoke-WebRequest -Method Get -Uri $downloadUrl -Headers @{Authorization = 'Basic ' + $logicalToken} -OutFile ("c:\temp\" + ($random) + ".pdf")
Hi Dave,
Thank you for this workaround, it works but now my problem is to get the token, because I need to be able to use other language than powershell (like python).
Currently my workaround is every week I have to use Get-PWConnectionClientToken from pwps_dab to get a token and store it in a file. Then I can read it from my script and use it.
It's not convenient and not secure.
It would be much better if I could directly get the token from Bentley IMS using oauth2 / imsoidc. But I'm very not familiar with that, and I didn't find any documentation from bentley.
Our goal is to integrate our in-house applications with ProjectWise. And we want to do it using Rest API, because some of apps are desktop apps, but other are web apps.
We've had other people asking about this and I came up with this approach. This example is PowerShell, but I think will be pretty easy to port to Python. I have not had any luck once ADFS gets involved, but YMMV.
The example gets a token that is valid for use with WSG for doing a file download and for passing to a regular API-based login.
---[PowerShell Source below]----
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$infoBit = ConvertTo-Json -Depth 4 @{ AppliesTo = "">connect-bts.bentley.com" RequestType = "">schemas.microsoft.com/.../issue" KeyType = "">schemas.microsoft.com/.../bearer" }
$Text = "Your.User@company.com:YourPassword"$Bytes = [System.Text.Encoding]::UTF8.GetBytes($Text)$EncodedText =[Convert]::ToBase64String($Bytes)
$authVal = "Basic " + $EncodedText
$imsUrl="">ims.bentley.com/.../IssueEx"
$resp = Invoke-RestMethod -ContentType "Application/Json" -Method Post -Body $infoBit -Uri $imsUrl -Headers @{"AUTHORIZATION"=$authVal}
$xmlDoc = New-Object System.Xml.XmlDocument$xmlDoc.LoadXml($resp.RequestedSecurityToken)
$cert = $xmlDoc.SelectSingleNode("//*[local-name()='X509Certificate']")
$base64EncodedCertificate = $cert.'#text'
$imsDelegatedUrl="">ims.bentley.com/.../IssueEx"
$authValueDelegated = "X509 access_token=" + $base64EncodedCertificate
$infoBitDelegated = ConvertTo-Json -Depth 4 @{ AppliesTo = "sso://pw-integration-server/1016" RequestType = "">schemas.microsoft.com/.../issue" KeyType = "">schemas.microsoft.com/.../bearer" ActAs = $resp.RequestedSecurityToken AppliesToBootstrapToken = "">connect-bts.bentley.com" }
$resp2 = Invoke-RestMethod -ContentType "Application/Json" -Method Post -Body $infoBitDelegated -Uri $imsDelegatedUrl -Headers @{"AUTHORIZATION"=$authValueDelegated}
#cmdlet from PWPS_DAB
$encToken2 = ConvertTo-EncodedToken $resp2.RequestedSecurityToken
#example URL
Invoke-WebRequest -Method Get -Uri $downloadUrl -Headers @{Authorization = 'Token ' + $encToken2} -OutFile ("c:\temp\" + ($random) + ".pdf")
New-PWLogin "decide-pwce-us.bentley.com:decide-pwce-us-10" -BentleyIMS -Token $resp2.RequestedSecurityToken
I am looking for more general approaches, but this is what I've gotten to at this point. I hope is helpful.
Dave
I tested it, and I confirm that it works well... when you are not federated.
We don't use ADFS but Azure AD for federation. The issue seems to be that ims.bentley.com request something like xml from the identity provider, instead of login / password credentials.
I'm not sure this will help but I am trying to do something parallel to other vendors using oaauth2 and STS in a Django environment.
The authorisation package provides azure, google, facebook,.... out of the box. We had to cusomise for AutoDesk BIM360 and ArcGIS online but it was pretty straight forward.
Here's the code to do third party login for autodesk on top of django.allauth:
provider.py
class AutodeskAccount(ProviderAccount): def get_profile_url(self): return self.account.extra_data.get('profileUrl') def get_avatar_url(self): return self.account.extra_data.get('avatar') def to_str(self): dflt = super(AutodeskAccount, self).to_str() return self.account.extra_data.get('username', dflt) class AutodeskProvider(OAuth2Provider): id = 'autodesk' name = 'Autodesk Forge' account_class = AutodeskAccount #['data.read', 'viewable.read', 'bucket:create', 'bucket:read', 'data:write'] # 'data.read', 'viewable.read'] scope = ['data:read', 'viewables.read', 'bucket:create', 'bucket:read', 'data:write'] def extract_uid(self, data): return str(data['userId']) def extract_common_fields(self, data): return dict(username=data.get('username'), name=data.get('displayName'), email=data.get('email')) def get_default_scope(self): scope = self.scope return scope providers.registry.register(AutodeskProvider)
views.py
class AutodeskOAuth2Adapter(OAuth2Adapter): provider_id = AutodeskProvider.id headers = {"User-Agent": "django-allauth-header"} access_token_url = 'https://developer.api.autodesk.com/authentication/v1/gettoken' authorize_url = 'https://developer.api.autodesk.com/authentication/v1/authorize' profile_url = 'https://developer.api.autodesk.com/userprofile/v1/users/@me' # After successfully logging in, use access token to retrieve user info def complete_login(self, request, app, token, **kwargs): headers = {"Authorization": "Bearer " + token.token} headers.update(self.headers) extra_data = requests.get(self.profile_url, headers=headers) # This only here because of weird response from the test suite if isinstance(extra_data, list): extra_data = extra_data[0] return self.get_provider().sociallogin_from_response( request, extra_data.json() ) oauth2_login = OAuth2LoginView.adapter_view(AutodeskOAuth2Adapter) oauth2_callback = OAuth2CallbackView.adapter_view(AutodeskOAuth2Adapter)
Headed out on vacation, but I will delve into the Azure AD and ADFS problems when I return in a couple of weeks. Will share if I have any luck.