Pass SauceLabs username/accesskey into DriverOptions class - c#

I am trying to send selenium tests to saucelabs using the DriverOptions class. According to this link, you need a sauce:options configuration, and according to this post a Dictionary will do. Here is my setup:
DriverOptions options = new ChromeOptions
{
PlatformName = "Windows 10",
BrowserVersion = "latest"
};
IDictionary<string, string> sauceOptions = new Dictionary<string, string>
{
{ "username", SauceUsername },
{ "accessKey", SauceAccessKey },
{ "name", TestContext.TestName },
{ "seleniumVersion", "3.11.0" }
};
options.AddAdditionalCapability("sauce:options", sauceOptions);
_driver = new RemoteWebDriver(new Uri("http://#ondemand.saucelabs.com:80/wd/hub"),
options.ToCapabilities(), TimeSpan.FromSeconds(600));
I get a WebDriverException on the RemoteWebDriverinit, saying Misconfigured -- Sauce Labs Authentication Error. You used username 'None' and access key 'None' to authenticate. This is weird because
I am given back the desired caps I used, which were:
The following desired capabilities were received:
{'browserName': 'chrome',
'browserVersion': 'latest',
'goog:chromeOptions': {'sauce:options': {'accessKey': 'XXXXXXXX-XXXX-XXXX-XXXX-XXXX163edf42',
'name': 'DriverOptionsTest',
'seleniumVersion': '3.11.0',
'username': 'kroe761'}},
'platformName': 'Windows 10'}
The last few digits of my accesskey are right and that is my username, so clearly I sent the correct credentials
If I remove the dictionary and pass the username and accesskey directly into the RemoteDriver uri (http://{SauceUsername}:{SauceAccessKey}#ondemand...) it works, However, I can't pass in any other sauce options.
Thanks!

Use the AddAdditionalCapability overload that takes three arguments, not two. This tells the ChromeOptions instance to add the dictionary to the top-level of the JSON payload, rather than as part of the goog:chromeOptions property. Here is what that would look like:
// Note, you must use the specific class here, rather than the
// base class, as the base class does not have the proper method
// overload. Also, the UseSpecCompliantProtocol property is required.
ChromeOptions options = new ChromeOptions
{
PlatformName = "Windows 10",
BrowserVersion = "latest",
UseSpecCompliantProtocol = true
};
Dictionary<string, object> sauceOptions = new Dictionary<string, object>
{
{ "username", SauceUsername },
{ "accessKey", SauceAccessKey },
{ "name", TestContext.TestName },
{ "seleniumVersion", "3.11.0" }
};
options.AddAdditionalCapability("sauce:options", sauceOptions, true);
_driver = new RemoteWebDriver(new Uri("http://ondemand.saucelabs.com:80/wd/hub"),
options.ToCapabilities(), TimeSpan.FromSeconds(600));

Related

How to use Pulumi Output<string> as a string in .NET

I have a basic Pulumi build for keycloak where I set up a realm, create a scope, create a client, and update teh scopes for my client.
class RealmBuild : Stack
{
public RealmBuild()
{
var realm = new Realm("ExampleRealm-realm", new RealmArgs
{
RealmName = "ExampleRealm"
});
var recipemanagementScope = ScopeFactory.CreateScope(realm.Id, "recipe_management");
var recipeManagementPostmanMachineClient = ClientFactory.CreateClientCredentialsFlowClient(realm.Id,
"recipe_management.postman.machine",
"974d6f71-d41b-4601-9a7a-a33084484682",
"RecipeManagement Postman Machine",
"https://oauth.pstmn.io");
recipeManagementPostmanMachineClient.ExtendDefaultScopes(recipemanagementScope.Name);
}
}
public static class ClientExtensions
{
public static void ExtendDefaultScopes(this Client client, params Output<string>[] scopeNames)
{
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
}
}
public class ClientFactory
{
public static Client CreateClientCredentialsFlowClient(Output<string> realmId,
string clientId,
string clientSecret,
string clientName,
string baseUrl)
{
return new Client($"{clientName.ToLower()}-client", new ClientArgs()
{
RealmId = realmId,
ClientId = clientId,
Name = clientName,
StandardFlowEnabled = false,
Enabled = true,
ServiceAccountsEnabled = true,
AccessType = "CONFIDENTIAL",
BaseUrl = baseUrl,
AdminUrl = baseUrl,
ClientSecret = clientSecret,
BackchannelLogoutSessionRequired = true,
BackchannelLogoutUrl = baseUrl
});
}
}
The problem is, I am getting this error around my scopes:
Diagnostics:
keycloak:openid:ClientDefaultScopes (default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.
To get the value of an Output<T> as an Output<string> consider:
1. o.Apply(v => $"prefix{v}suffix")
2. Output.Format($"prefix{hostname}suffix");
See https://pulumi.io/help/outputs for more details.
This function may throw in a future version of Pulumi.):
error: Duplicate resource URN 'urn:pulumi:dev::KeycloakPulumiStack::keycloak:openid/clientDefaultScopes:ClientDefaultScopes::default-scopes-for-Calling [ToString] on an [Output<T>] is not supported.
To get the value of an Output<T> as an Output<string> consider:
1. o.Apply(v => $"prefix{v}suffix")
2. Output.Format($"prefix{hostname}suffix");
See https://pulumi.io/help/outputs for more details.
This function may throw in a future version of Pulumi.'; try giving it a unique name
I tried something like this as well var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");, but I can't pass that into the name for ClientDefaultScopes
I did look at the docs to see if anything stuck out as an issue, but I'm clearly missing something.
Rule number 1 with Pulumi outputs: Anything you return from an apply() will still be an Output, even if it looks like it should be a string.
In other words, on this line of code:
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
defaultScopeName is Output<string>.
However, the x variable in the lambda is in fact a string rather than an output.
The other item to note is that the name of a resource (so the first argument) cannot be an Output. So in your code:
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
because defaultScopeName is an Output, this won't work.
You could create the resource inside of the apply():
var defaultScopea = $"default-scopes-for-{client.Name.Apply(x =>
return new ClientDefaultScopes(x, new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes =
{
"openid",
"profile",
"email",
"roles",
"web-origins",
scopeNames,
},
});
)}";
however, this may mean that the resource won't appear in any previews (see the note in the Apply section of the Inputs and Outputs page in the Pulumi docs).
So what's the answer here? it looks like you're setting the ClientName to be a string value earlier in the code, so I'd use the same variable that you're setting there.
You can't mix and match string and Output<string> values. Instead, you need to transform any output and append your static list to the list of resolved values:
var defaultScopeName = Output.Format($"default-scopes-for-{client.Name}");
var defaultScopes = new ClientDefaultScopes("some-scope-name", new ClientDefaultScopesArgs()
{
RealmId = client.RealmId,
ClientId = client.Id,
DefaultScopes = Output.All(scopeNames).Apply(names =>
new[] { "openid", "profile", "email", "roles", "web-origins", }
.Concat(names)),
});
Note that Output.Format is used for string formatting, Output.All is used to convert to Output<string[]> and .Apply is used to transform the array. You can learn more in Inputs and Outputs.
Currently, Pulumi only supports string types for the name of a resource.
Since
var defaultScopeName = $"default-scopes-for-{client.Name.Apply(x => x)}";
is using an output of a resource, defaultScopeName is type Output<string> and can't be used for the resource name in the line,
var defaultScopes = new ClientDefaultScopes(defaultScopeName, new ClientDefaultScopesArgs()
If I'm reading the code correctly, you specify clientName and use it to set client.Name. So, I would just pass in clientName and use that instead of client.Name. And, that should work since it's a basic type all the way through.

Add MS Teams website tab using graph API error

Context:
I am trying to add a new website tab to an existing channel in MS Teams and then get the id of newly-created tab.
Problem:
I am able to create new tab but I am getting a "BadRequest" exception from the Graph:
Message: Value cannot be null. Parameter name: entity
The interesting part is that the tab is created and visible in MS Teams in the correct team and channel but I cannot get it's id in any way.
My code:
var tab = await _graphClient.Teams[teamId].Channels[channelId].Tabs.Request().WithMaxRetry(3).AddAsync(
new TeamsTab
{
DisplayName = "New Tab",
AdditionalData = new Dictionary<string, object>
{
["teamsApp#odata.bind"] =
$"{_teamsFactory.GraphV1Endpoint}/appCatalogs/teamsApps/com.microsoft.teamspace.tab.web"
},
Configuration = new TeamsTabConfiguration
{
EntityId = null,
WebsiteUrl = $"{_appUrl}/1",
ContentUrl = $"{_appUrl}/1",
RemoveUrl = null,
}
}
);
Like I wrote above, this code works and the tab is created but GraphServiceClient throws an exception before the tab variable is assigned.
And when I tried to get the tab list in Graph Explorer
https://graph.microsoft.com/v1.0/teams/{teamid}/channels/{channelid}/tabs
I received an error response:
{
"error": {
"code": "InternalServerError",
"message": "Failed to execute request.",
"innerError": {
"request-id": "a03654e8-37a7-4fbb-8052-6a1b11721234",
"date": "2020-02-24T15:11:54"
}
}
}
I think you might need to set a value for "EntityId" - basically just a string value to uniquely "name" your tab. It's not the "DisplayName", more a string "id" for the tab.
POST https://graph.microsoft.com/v1.0/teams/{id}/channels/{id}/tabs
{
"displayName": "My Contoso Tab",
"teamsApp#odata.bind" : "https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/06805b9e-77e3-4b93-ac81-525eb87513b8",
"configuration": {
"entityId": "2DCA2E6C7A10415CAF6B8AB6661B3154",
"contentUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154/tabView",
"websiteUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154",
"removeUrl": "https://www.contoso.com/Orders/2DCA2E6C7A10415CAF6B8AB6661B3154/uninstallTab"
}
}
Please take a look at Add Tab to a channel using Graph API
Edit 1: Could you please check you have appropriate permissions to add the Tab?
Edit2: Could you please try below piece of code?
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var teamsTab = new TeamsTab
{
DisplayName = "WebsiteTab",
AdditionalData = new Dictionary<string, object>()
{
{"teamsApp#odata.bind","https://graph.microsoft.com/v1.0/appCatalogs/teamsApps/com.microsoft.teamspace.tab.web"}
},
Configuration = new TeamsTabConfiguration
{
EntityId = null,
ContentUrl = "https://learn.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bots-context",
RemoveUrl = null,
WebsiteUrl = "https://learn.microsoft.com/en-us/microsoftteams/platform/resources/bot-v3/bots-context"
}
};
await graphClient.Teams["TeamId"].Channels["ChannelId"].Tabs
.Request()
.AddAsync(teamsTab);
Finally I found the "solution" though a better name is a workaround for my issue. To make it work I had to set ODataType to null in TeamsTabConfiguration. That's all. The code should look like this:
var tab = await _graphClient.Teams[teamId].Channels[channelId].Tabs.Request().WithMaxRetry(3).AddAsync(
new TeamsTab
{
DisplayName = TabTitle,
ODataBind = $"{_teamsFactory.GraphV1Endpoint}/appCatalogs/teamsApps/com.microsoft.teamspace.tab.web",
Configuration = new TeamsTabConfiguration
{
ODataType = null,
EntityId = null,
WebsiteUrl = $"{_appUrl}/1",
ContentUrl = $"{_appUrl}/1",
RemoveUrl = null
}
});
Like I mentioned it is only a workaround. It is labeled as "service bug" on GitHub (issue#598)

C# POST Request with arrays being sent

I want to send a POST request in c# and i need the following sent through
"jsonrpc": "2.0",
"id": "12345",
"method": "my method",
"params": {
"api_key": "my api key",
"preset_id": "my preset id"
}
I tried using
using (WebClient client = new WebClient ())
{
byte [] response =
client.UploadValues ("my url", new NameValueCollection ()
{
{ "jsonrpc", "2.0" },
{ "id", "12345"},
{ "method", "my method"},
{ "params", ""}
});
string result = System.Text.Encoding.UTF8.GetString (response);
}
But i couldnt make the params an array, Please help, Thank you
It appears that you are asking for the parameters to be in an array, but they are actually shown as a "subclass". If the values were in an array, they should have square brackets around them.
However, both results are easy to achieve using anonymous (or real) classes (which I much prefer over embedding the property names in quoted text (makes future modifications much easier to implement).
var parameters = new
{
api_key = "my api key",
preset_id = "my preset id"
};
var json = new
{
jsonrpc = "2.0",
id = "12345",
method = "my method",
#params = parameters
};
string sResult = (new System.Web.Script.Serialization.JavaScriptSerializer()).Serialize(json);
The above code will result in the same output that you have shown. If you want an actual array instead, you can change the parameters definition to:
var parameters = new NameValueCollection();
parameters.Add("api_key", "my api key");
parameters.Add("preset_id", "my preset id");
Note that I used the .Net framework json serializer (from System.Web.Extensions), but you can use the serializer of your choice (we generally use NewtonSoft's JsonConvert).

IdentityServer "invalid_client" error always returned

I'm trying to use IdentityServer3, but don't know why I'm getting "invalid_client" error always, always no matter what I do.
This is the code I'm using:
//Startup.cs (Auth c# project)
public void Configuration(IAppBuilder app) {
var inMemoryManager = new InMemoryManager();
var factory = new IdentityServerServiceFactory()
.UseInMemoryClients(inMemoryManager.GetClients())
.UseInMemoryScopes(inMemoryManager.GetScopes())
.UseInMemoryUsers(inMemoryManager.GetUsers());
var options = new IdentityServerOptions {
Factory = factory,
RequireSsl = false
};
app.UseIdentityServer(options);
}
InMemoryManager helper.
//InMemoryManager.cs
public class InMemoryManager {
public List<InMemoryUser> GetUsers() {
return new List<InMemoryUser> {
new InMemoryUser {
Username = "alice",
Password = "password",
Subject = "2",
Claims = new [] {
new Claim("User name", "Alice")
}
}
};
}
public IEnumerable<Scope> GetScopes() {
return new[] {
new Scope {
Name = "api1",
DisplayName = "API 1"
}
};
}
public IEnumerable<Client> GetClients() {
return new[] {
new Client {
ClientName = "Silicon on behalf of Carbon Client",
ClientId = "carbon",
Enabled = true,
//AccessTokenType = AccessTokenType.Reference,
Flow = Flows.ResourceOwner,
ClientSecrets = new List<Secret> {
new Secret("secret".Sha256())
},
AllowedScopes = new List<string> {
"api1"
}
}
};
}
}
This is the result I always get.
I'm using postman to try the Auth Server, but I always get that error. I've read another solutions but none seeme to works, I don't know what else to try.
Cheers.
Just add the client_secret: secret in your Body. It will work!
Late answer, but for me this happened following the IdentityServer 4 tutorial when trying to log in with a username and password. I used the code from the first tutorial (using client credentials), and modified the client to use passwords. Afterwards, I kept getting this error.
To fix it, in the IdentityServer project, config.cs, in the GetClients method, set AllowedGrantTypes to GrantTypes.ResourceOwnerPassword, and change ClientId from client to ro.client (or whatever the client name is that you use in the Client project's program.cs).
Your request shoud be as follows:
Authorisation header with clientId/clientSecret. carbon/secret in Your case.
In Body. username/password shoud be alice/password in Your case. If Your don't need to refresh tokens, You might exclude offline_access scope from request.

C# Selenium WebDriver FireFox Profile - using proxy with Authentication

When you set proxy server parameter in the code below if your proxy server requires authentication then FireFox will bring Authentication dialog and basically you can't fill it in automatically.
So is there is anyway to set USERNAME and PASSWORD ?
FirefoxProfile profile = new FirefoxProfile();
String PROXY = "192.168.1.100:8080";
OpenQA.Selenium.Proxy proxy = new OpenQA.Selenium.Proxy();
proxy.HttpProxy=PROXY;
proxy.FtpProxy=PROXY;
proxy.SslProxy=PROXY;
profile.SetProxyPreferences(proxy);
FirefoxDriver driver = new FirefoxDriver(profile);
If you try to format proxy string to something like that http://username:pass#192.168.1.1:8080
You get error that string is invalid. So I wonder there is must be a way of achieving this.
Any help would be appreciated.
String PROXY = "http://login:pass#proxy:port";
ChromeOptions options = new ChromeOptions();
options.AddArguments("user-data-dir=path/in/your/system");
Proxy proxy = new Proxy();
proxy.HttpProxy = PROXY;
proxy.SslProxy = PROXY;
proxy.FtpProxy = PROXY;
options.Proxy = proxy;
// Initialize the Chrome Driver
using (var driver = new ChromeDriver(options))
You can write own firefox extension for proxy, and launch from selenium. You need write 2 files and pack it.
background.js
var proxy_host = "YOUR_PROXY_HOST";
var proxy_port = YOUR_PROXY_PORT;
var config = {
mode: "fixed_servers",
rules: {
singleProxy: {
scheme: "http",
host: proxy_host,
port: proxy_port
},
bypassList: []
}
};
function proxyRequest(request_data) {
return {
type: "http",
host: proxy_host,
port: proxy_port
};
}
browser.proxy.settings.set({value: config, scope: "regular"}, function() {;});
function callbackFn(details) {
return {
authCredentials: {
username: "YOUR_USERNAME",
password: "YOUR_PASSWORD"
}
};
}
browser.webRequest.onAuthRequired.addListener(
callbackFn,
{urls: ["<all_urls>"]},
['blocking']
);
browser.proxy.onRequest.addListener(proxyRequest, {urls: ["<all_urls>"]});
manifest.json
{
"name": "My Firefox Proxy",
"version": "1.0.0b",
"manifest_version": 2,
"permissions": [
"browsingData",
"proxy",
"storage",
"tabs",
"webRequest",
"webRequestBlocking",
"downloads",
"notifications",
"<all_urls>"
],
"background": {
"scripts": ["background.js"]
},
"browser_specific_settings": {
"gecko": {
"id": "myproxy#example.org"
}
}
}
Next you need packed this files to zip archive in DEFLATED mode with .xpi at end like my_proxy_extension.xpi.
You have two choices:
Sign your extension Here you can read more about verify extension and extension's structure
OR
Run unsigned. For this step:
Open firefox flags at about:config and set options xpinstall.signatures.required to false
OR
Update firefox profile in:
Windows: C:\Program Files\Mozilla Firefox\defaults\pref\channel-prefs.js
Linux: /etc/firefox/syspref.js
Add next line to end of file:
pref("xpinstall.signatures.required",false);
After this steps run selenium and install this extension:
FirefoxProfile profile = new FirefoxProfile();
profile.addExtension(new File("path/to/my_proxy_extension.xpi"));
driver = new FirefoxDriver(profile);
What you can do is to create a profile and save the authentication data in it.
If your profile is called "webdriver" you can select it from your code in the initialization:
ProfilesIni allProfiles = new ProfilesIni();
FirefoxProfile profile = allProfiles.getProfile("WebDriver");
profile.setPreferences("foo.bar",23);
WebDriver driver = new FirefoxDriver(profile);
Did it with MS UI Automation without AutoIt:
public void AuthInProxyWindow (string login, string pass)
{
var proxyWindow = AutomationElement.RootElement
.FindFirst(TreeScope.Subtree,
new PropertyCondition(AutomationElement.ClassNameProperty, "MozillaDialogClass"));
var edits = proxyWindow.FindAll(TreeScope.Subtree,
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit));
var unamePoint = edits[1].GetClickablePoint();
Mouse.MoveTo(new Point((int) unamePoint.X, (int) unamePoint.Y));
Mouse.Click(MouseButton.Left);
SendKeys.SendWait(login);
var pwdPoint = edits[2].GetClickablePoint();
Mouse.MoveTo(new Point((int) pwdPoint.X, (int) pwdPoint.Y));
Mouse.Click(MouseButton.Left);
SendKeys.SendWait(pass);
Keyboard.Press(Key.Return);
Logger.Debug("Authefication in Firefox completed succesfully");
}
Mouse moves by Microsoft.TestApi
To stop firefox from giving you the auth pop up simple make sure you set your proxy URL to include the auth details in the setup stage as below:
var myProxy = user + ":" + pass + "#" + proxyIP + ":" + proxyPORT;
options.SetPreference("network.proxy.type", 1);
options.SetPreference("network.proxy.http", myProxy);
options.SetPreference("network.proxy.http_port", proxyPORT);
options.SetPreference("general.useragent.override", useragent);
driver = new FirefoxDriver(driverService, options);

Categories

Resources