From 2bfa23f83818defa50abeb153f10b0b3204c6d30 Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 09:06:01 -0500 Subject: [PATCH 1/6] Skip AWS tests in non-AWS environments --- build.sh | 9 ++++++++- conjur-api/AWSIAMAuthenticator.cs | 6 ------ docker/build.sh | 6 ++++++ docker/tag | 2 +- secrets.yml | 1 + test/AWSAuthenticatorTest.cs | 9 +++++++-- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/build.sh b/build.sh index 0afb6a5..62168ff 100755 --- a/build.sh +++ b/build.sh @@ -19,7 +19,14 @@ if [ ! -f "$PWD/VERSION" ]; then fi CIDFILE=$(mktemp -u) -docker run -v "$PWD":/src:ro --cidfile="$CIDFILE" -e WRITE_ARTIFACTORY_USERNAME -e WRITE_ARTIFACTORY_PASSWORD -e WRITE_ARTIFACTORY_URL "$TAG" +docker run \ + -v "$PWD":/src:ro \ + --cidfile="$CIDFILE" \ + -e WRITE_ARTIFACTORY_USERNAME \ + -e WRITE_ARTIFACTORY_PASSWORD \ + -e WRITE_ARTIFACTORY_URL \ + -e RUN_AWS_TESTS \ + "$TAG" CID=$(cat "$CIDFILE") diff --git a/conjur-api/AWSIAMAuthenticator.cs b/conjur-api/AWSIAMAuthenticator.cs index 0b5551a..dc227ab 100644 --- a/conjur-api/AWSIAMAuthenticator.cs +++ b/conjur-api/AWSIAMAuthenticator.cs @@ -72,12 +72,6 @@ private Credentials GetSTSToken() // For now this can be handled by assuming the same role and allowing the AssumeRole permission for the role // for itself. - // TODO: Handle case where we're running on an EC2 instance that has the assigned role equal to the role - // used to authenticate to Conjur. Then we don't need to assume a different role and we already have - // the session token we need. - // For now this can be handled by assuming the same role and allowing the AssumeRole permission for the role - // for itself. - if (!String.IsNullOrEmpty(this.conjurIAMRole)) { var assumeRoleReq = new AssumeRoleRequest() diff --git a/docker/build.sh b/docker/build.sh index dac0e6d..2a953fc 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -29,4 +29,10 @@ dotnet test --logger:"junit;LogFileName=/build/TestResults.xml" VERSION=$(project_version) dotnet build api-dotnet.sln --configuration Release /p:AssemblyVersion="$VERSION" +# publish +if [ -z "${WRITE_ARTIFACTORY_URL:-}" ]; then + echo "WRITE_ARTIFACTORY_URL is not set, skipping nuget push" + exit 0 +fi + docker/nuget.sh "$VERSION" diff --git a/docker/tag b/docker/tag index b869ea7..eb5a5bb 100644 --- a/docker/tag +++ b/docker/tag @@ -1 +1 @@ -conjurinc/dotnet-build:20230818T191439Z +conjurinc/dotnet-build:20241122T134332Z diff --git a/secrets.yml b/secrets.yml index d66cdbe..ce4149d 100644 --- a/secrets.yml +++ b/secrets.yml @@ -2,3 +2,4 @@ pipeline: WRITE_ARTIFACTORY_USERNAME: !var ci/artifactory/users/jenkins/username WRITE_ARTIFACTORY_PASSWORD: !var ci/artifactory/users/jenkins/password WRITE_ARTIFACTORY_URL: !var ci/artifactory/url + RUN_AWS_TESTS: true diff --git a/test/AWSAuthenticatorTest.cs b/test/AWSAuthenticatorTest.cs index b4275e7..6dad95d 100644 --- a/test/AWSAuthenticatorTest.cs +++ b/test/AWSAuthenticatorTest.cs @@ -9,10 +9,15 @@ namespace Conjur.Test public class AWSAuthenticatorTest : Base { [Test] - // Uncomment the [Ignore] attribute to skip this test when not running in AWS - // [Ignore("This test requires an AWS environment")] public void AuthenticateTest() { + // Check if we're running in an AWS environment. This environment variable is set by Summon when run in the Jenkins pipeline, + // since it's defined in secrets.yml under the 'ci' environment. If it's not set, we ignore the test. + if (Environment.GetEnvironmentVariable("RUN_AWS_TESTS") != "true") + { + Assert.Ignore("This test requires an AWS environment to run. Set the RUN_AWS_TESTS environment variable to 'true' to run this test."); + } + string conjurIdentity = "somehost"; string conjurAuthenticator = "authn-iam/test"; // This role must be the role assigned to the EC2 instance where this test is running. From 3157570c86322ce9974f4ddd0077ebf61841699c Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 09:06:08 -0500 Subject: [PATCH 2/6] Update readme --- README.md | 182 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 101 insertions(+), 81 deletions(-) diff --git a/README.md b/README.md index 0037639..de0115c 100644 --- a/README.md +++ b/README.md @@ -6,90 +6,104 @@ invoking our Conjur API to perform operations on stored data (add, retrieve, etc ## Table of Contents -- [Using this Project With Conjur Open Source](#Using-conjur-api-dotnet-with-Conjur-Open-Source) +- [Using this Project With Conjur Open Source](#using-conjur-api-dotnet-with-conjur-open-source) - [Requirements](#requirements) - [Building](#building) - [Methods](#methods) -- [Example](#example) +- [Examples](#examples) - [Contributing](#contributing) - [License](#license) -## Using conjur-api-dotnet with Conjur Open Source +## Using conjur-api-dotnet with Conjur Open Source -Are you using this project with [Conjur Open Source](https://github.com/cyberark/conjur)? Then we -**strongly** recommend choosing the version of this project to use from the latest [Conjur OSS -suite release](https://docs.conjur.org/Latest/en/Content/Overview/Conjur-OSS-Suite-Overview.html). -Conjur maintainers perform additional testing on the suite release versions to ensure -compatibility. When possible, upgrade your Conjur version to match the -[latest suite release](https://docs.conjur.org/Latest/en/Content/ReleaseNotes/ConjurOSS-suite-RN.htm); -when using integrations, choose the latest suite release that matches your Conjur version. For any +Are you using this project with [Conjur Open Source](https://github.com/cyberark/conjur)? Then we +**strongly** recommend choosing the version of this project to use from the latest [Conjur OSS +suite release](https://docs.conjur.org/Latest/en/Content/Overview/Conjur-OSS-Suite-Overview.html). +Conjur maintainers perform additional testing on the suite release versions to ensure +compatibility. When possible, upgrade your Conjur version to match the +[latest suite release](https://docs.conjur.org/Latest/en/Content/ReleaseNotes/ConjurOSS-suite-RN.htm); +when using integrations, choose the latest suite release that matches your Conjur version. For any questions, please contact us on [Discourse](https://discuss.cyberarkcommons.org/c/conjur/5). ## Requirements -- Conjur Enterprise (formerly DAP) v10+ or Conjur Open Source v1+ - -For Conjur Enterprise V4, use the [V4 branch](https://github.com/cyberark/conjur-api-dotnet/tree/v4) +- Conjur Enterprise v10+ or Conjur Open Source v1+ - When using the **AWS Authenticator**, Conjur Enterprise v13+ or Conjur Cloud (Conjur OSS was not tested) ## Building -This sample was built and tested with Visual Studio 2015. +### Visual Studio To load in Visual Studio, from the Visual Studio File menu select Open > Project/Solution > api-dotnet.sln - and build the solution. This will create: +and build the solution. This will create: + +- conjur-api.dll: the .NET version of the Conjur API. +- ConjurTest.dll: test DLL used for automated testing of the Conjur .NET API +- example.exe: sample application that uses the Conjur API. - - conjur-api.dll: the .NET version of the Conjur API. - - ConjurTest.dll: test DLL used for automated testing of the Conjur .NET API - - example.exe: sample application that uses the Conjur API. +### Docker -Optionally, to build in a Docker container, it is recommended to use Mono and xbuild. +To build in a Docker container, run the following commands: + +```bash +make -C docker +./build.sh +``` ## Methods ### `Client` #### `Client Client(uri, account)` + - Create new Conjur instance - - `uri` - URI of the Conjur server. Example: `https://myconjur.org.com/api` - - `account` - Name of the Conjur account + - `uri` - URI of the Conjur server. Example: `https://myconjur.org.com/api` + - `account` - Name of the Conjur account #### `void client.LogIn(string userName, string password)` + - Login to a Conjur user - - `userName` - Username of Conjur user to login as - - `password` - Password of user + - `userName` - Username of Conjur user to login as + - `password` - Password of user #### `void client.TrustedCertificates.ImportPem (string certPath)` + - Add Conjur root certificate to system trust store - - `certPath` = Path to cert + - `certPath` = Path to cert #### `void client.DisableCertCheck()` + - Disable SSL Cert check -- used when Conjur is configured with self-signed cert. Do not use in production. #### `void client.EnableCertCheck()` -- Enable SSL Cert check -- Default is to perform cert check; this method is used if there is a need to disable and enable the cert check. +- Enable SSL Cert check -- Default is to perform cert check; this method is used if there is a need to disable and enable the cert check. #### `client.Credential = new NetworkCredential(string userName, string apiKey)` + - To login with an API key, use it directly - - `userName` - Username of user to login as - - `apiKey` - API key of user/host/etc + - `userName` - Username of user to login as + - `apiKey` - API key of user/host/etc #### `IEnumerable client.ListVariables(string query = null)` + - Returns a list of variable objects - - `query` - Additional query parameters (not required) + - `query` - Additional query parameters (not required) #### `uint client.CountVariables(string query = null)` + - Return count of Conjur variables conforming to the `query` parameter - - `query` - Additional query parameters (not required) + - `query` - Additional query parameters (not required) #### `Host client.CreateHost(string name, string hostFactoryToken)` + - Creates a host using a host factory token - - `name` - Name of the host to create - - `hostFactoryToken` - Host factory token + - `name` - Name of the host to create + - `hostFactoryToken` - Host factory token #### `client.Authenticator = new Conjur.AWSIAMAuthenticator(Conjur.Client client, string Identity, string Authenticator, string roleArn = "", string ConjurAWSRegion = "us-east-1")` + - **REQUIREMENTS**: Conjur Enterprise v13+ or Conjur Cloud (Conjur OSS was not tested) - Configure the client to use the AWS IAM Authenticator - Client must be instantiated with these attributes before instantiating the AWS authenticator: @@ -100,82 +114,88 @@ Optionally, to build in a Docker container, it is recommended to use Mono and xb ### `Policy` #### `Policy client.Policy(string policyName)` -- Create a Conjur policy object - - `policyName` - Name of policy + +- Create a Conjur policy object + - `policyName` - Name of policy #### `policy.LoadPolicy(Stream policyContent)` + - Load policy into Conjur - - `policyContent` - The policy + - `policyContent` - The policy ### `Variable` #### `Variable client.Variable(string name)` + - Instantiate a Variable object - - `name` - Name of the variable + - `name` - Name of the variable #### `Boolean variable.Check(string privilege)` + - Check if the current entity has the specified privilege on this variable - - `privilege` - string name of the privilege to check for - - Privileges: read, create, update, delete, execute + - `privilege` - string name of the privilege to check for + - Privileges: read, create, update, delete, execute #### `void variable.AddSecret(bytes val)` + - Change current variable to val - - `val` - Value in bytes to update current variable to + - `val` - Value in bytes to update current variable to #### `String variable.GetValue()` + - Return the value of the current Variable ## Examples -#### Example Code +### Example Code ```csharp - // Instantiate a Conjur Client object. - // parameter: URI - conjur appliance URI - // parameter: ACCOUNT - conjur account name - // return: Client object - if URI is incorrect errors thrown when used - Client conjurClient = new Client("https://myorg.com", account); - - // Login with Conjur credentials like userid and password, - // or hostid and api_key, etc - // parameters: username - conjur user or host id for example - // password - conjur user password or host api key for example - string conjurAuthToken = conjurClient.Login(username, password); - - // Check if this user has permission to get the value of variableId - // That requires execute permissions on the variable - - // Instantiate a Variable object - // parameters: client - contains authentication token and conjur URI - // name - the name of the variable - Variable conjurVariable = new Variable(conjurClient, variableId); - - // Check if the current user has "execute" privilege required to get - // the value of the variable - // parameters: privilege - string name of the priv to check for - bool isAllowed = conjurVariable.Check("execute"); - if (!isAllowed) - { - Console.WriteLine("You do not have permissions to get the value of {0}", variableId); - } - else - { - Console.WriteLine("{0} has the value: {1}", variableId, conjurVariable.GetValue()); - } +// Instantiate a Conjur Client object. +// parameter: URI - conjur appliance URI +// parameter: ACCOUNT - conjur account name +// return: Client object - if URI is incorrect errors thrown when used +Client conjurClient = new Client("https://myorg.com", account); + +// Login with Conjur credentials like userid and password, +// or hostid and api_key, etc +// parameters: username - conjur user or host id for example +// password - conjur user password or host api key for example +string conjurAuthToken = conjurClient.Login(username, password); + +// Check if this user has permission to get the value of variableId +// That requires execute permissions on the variable + +// Instantiate a Variable object +// parameters: client - contains authentication token and conjur URI +// name - the name of the variable +Variable conjurVariable = new Variable(conjurClient, variableId); + +// Check if the current user has "execute" privilege required to get +// the value of the variable +// parameters: privilege - string name of the priv to check for +bool isAllowed = conjurVariable.Check("execute"); +if (!isAllowed) +{ + Console.WriteLine("You do not have permissions to get the value of {0}", variableId); +} +else +{ + Console.WriteLine("{0} has the value: {1}", variableId, conjurVariable.GetValue()); +} ``` -#### Example App +### Example App This example app shows how to: - - Authenticate - - Load Policy - - Check permissions to get the value of a variable - - Get the value of a variable - - Use a Host Factory token to create a new Host and get an apiKey to use with Conjur +- Authenticate +- Load Policy +- Check permissions to get the value of a variable +- Get the value of a variable +- Use a Host Factory token to create a new Host and get an apiKey to use with Conjur To run the sample in Visual Studio, set the `example` project as the Startup - Project. To do so, in + Project. To do so, in the Solution Explorer right click over `example` and select `Set as Startup Project`. ```txt @@ -191,7 +211,7 @@ Usage: Example `applianceURL`: the applianceURL e.g. `https://conjur.myorg.com/` `applianceCertificatePath`: the path and name of the Conjur appliance - certificate. The easiest way to get the certifiate is to use the Conjur + certificate. The easiest way to get the certifiate is to use the Conjur CLI command `conjur init -u conjur.myorg.com -f .conjurrc`. The certificate can be taken from any system you have run the Conjur CLI from. `accountName`: The name of the account in Conjur. @@ -203,9 +223,9 @@ CLI command `conjur init -u conjur.myorg.com -f .conjurrc`. The certificate can `variableId`: The name of an existing variable in Conjur that has a value set and for which the `username` has execute permissions. `hostFactoryToken`: A host factory token. The easiest way to get a host - factory token for testing is to add a hostfactory to a layer using + factory token for testing is to add a hostfactory to a layer using the Conjur CLI command `conjur hostfactory create` and - `conjur hostfactory token create`. Take the token returned from that call + `conjur hostfactory token create`. Take the token returned from that call and pass it as the hostFactoryToken parameter to this example. #### Example Code with AWS Authenticator From 144b25b22cdb453326ea587220bd26d738dc2a85 Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 09:20:48 -0500 Subject: [PATCH 3/6] Hide irrelevant warnings --- conjur-api/Host.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conjur-api/Host.cs b/conjur-api/Host.cs index 0d95bfd..550e8be 100644 --- a/conjur-api/Host.cs +++ b/conjur-api/Host.cs @@ -18,12 +18,12 @@ namespace Conjur public class Host { // these data members are assigned by a deserializer - #pragma warning disable 169 + #pragma warning disable 169, 0649 [DataMember] private string id; [DataMember] private string api_key; - #pragma warning restore 169 + #pragma warning restore 169, 0649 private NetworkCredential credential; From 6be1fafa5741c501653d75e7432451603405b083 Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 09:33:10 -0500 Subject: [PATCH 4/6] Fix and re-enable tests Fixes https://github.com/cyberark/conjur-api-dotnet/issues/46 --- test/HostFactoryTest.cs | 62 ++++++++++++++++++++--------------------- test/ResourceTest.cs | 26 ++++++++--------- 2 files changed, 42 insertions(+), 46 deletions(-) diff --git a/test/HostFactoryTest.cs b/test/HostFactoryTest.cs index b9b590f..c493568 100644 --- a/test/HostFactoryTest.cs +++ b/test/HostFactoryTest.cs @@ -1,43 +1,43 @@ using System; -using System.Net; +using System.Linq; +using System.Net.Http; using NUnit.Framework; namespace Conjur.Test { public class HostFactoryTest : Base { - // TODO: This test has been commented out and will be fixed in Issue #46 (https://github.com/cyberark/conjur-api-dotnet/issues/46) - //[Test] - //public void TestCreateHost() - //{ - // Mocker.Mock(new Uri("test:///host_factories/hosts?id=test-host"), - // @"{ - // ""id"": ""test-host"", - // ""userid"": ""deputy/test-factory"", - // ""created_at"": ""2015-11-13T22:57:14Z"", - // ""ownerid"": ""test:group:ops"", - // ""roleid"": ""test:host:test-host"", - // ""resource_identifier"": ""test:host:test-host"", - // ""api_key"": ""14x82x72syhnnd1h8jj24zj1kqd2j09sjy3tddwxc35cmy5nx33ph7"" - // }") - // .Verifier = (WebRequest wr) => - // { - // Assert.AreEqual("POST", wr.Method); - // Console.WriteLine (wr.Headers ["Authorization"]); - // if (wr.Headers["Authorization"] != "Token token=\"host-factory-token\"") - // throw new WebMocker.MockResponseException(HttpStatusCode.Unauthorized, "Unauthorized"); - // }; + [Test] + public void TestCreateHost() + { + Mocker.Mock(new Uri("test:///host_factories/hosts?id=test-host"), + @"{ + ""id"": ""test-host"", + ""userid"": ""deputy/test-factory"", + ""created_at"": ""2015-11-13T22:57:14Z"", + ""ownerid"": ""test:group:ops"", + ""roleid"": ""test:host:test-host"", + ""resource_identifier"": ""test:host:test-host"", + ""api_key"": ""14x82x72syhnnd1h8jj24zj1kqd2j09sjy3tddwxc35cmy5nx33ph7"" + }") + .Verifier = (HttpRequestMessage requestMessage) => + { + Assert.AreEqual(HttpMethod.Post, requestMessage.Method); + Console.WriteLine(requestMessage.Headers.GetValues("Authorization").SingleOrDefault()); + if (requestMessage.Headers.GetValues("Authorization").SingleOrDefault() != "Token token=\"host-factory-token\"") + throw new UnauthorizedException("Unauthorized", new HttpRequestException("Unauthorized")); + }; - // Assert.Throws( - // () => Client.CreateHost("test-host", "bar")); + Assert.Throws( + () => Client.CreateHost("test-host", "bar")); - // var host = Client.CreateHost("test-host", "host-factory-token"); + var host = Client.CreateHost("test-host", "host-factory-token"); - // Assert.AreEqual("test-host", host.Id); - // Assert.AreEqual("host/test-host", host.Credential.UserName); - // Assert.AreEqual( - // "14x82x72syhnnd1h8jj24zj1kqd2j09sjy3tddwxc35cmy5nx33ph7", - // host.Credential.Password); - //} + Assert.AreEqual("test-host", host.Id); + Assert.AreEqual("host/test-host", host.Credential.UserName); + Assert.AreEqual( + "14x82x72syhnnd1h8jj24zj1kqd2j09sjy3tddwxc35cmy5nx33ph7", + host.Credential.Password); + } } } \ No newline at end of file diff --git a/test/ResourceTest.cs b/test/ResourceTest.cs index 28818d3..7139134 100644 --- a/test/ResourceTest.cs +++ b/test/ResourceTest.cs @@ -14,23 +14,19 @@ public ResourceTest() Client.Authenticator = new MockAuthenticator(); } - // TODO: This test has been commented out and will be fixed in Issue #46 (https://github.com/cyberark/conjur-api-dotnet/issues/46) - //[Test] - //public void TestCheck() - //{ - // var resource = Client.Resource(Kind, Name); + [Test] + public void TestCheck() + { + var resource = Client.Resource(Kind, Name); - // var mock = Mocker.Mock(new Uri("test:///resources/" + TestAccount - // + "/" + Kind + "/" + Name + "/?check=true&privilege=fry"), ""); - // Assert.IsTrue(resource.Check("fry")); + Mocker.Mock(new Uri("test:///resources/" + TestAccount + + "/" + Kind + "/" + Name + "/?check=true&privilege=fry"), ""); + Assert.IsTrue(resource.Check("fry")); - // mock.Verifier = (WebRequest) => - // { - // throw new WebMocker.MockResponseException( - // HttpStatusCode.Forbidden, "Forbidden"); - // }; - // Assert.IsFalse(resource.Check("fry")); - //} + Mocker.Mock(new Uri("test:///resources/" + TestAccount + + "/" + Kind + "/" + Name + "/?check=true&privilege=fry"), "", HttpStatusCode.Forbidden); + Assert.IsFalse(resource.Check("fry")); + } [Test] public void TestNameToId() From e1987df70d85ffbc63d169829f5fa160f00e6721 Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 09:58:35 -0500 Subject: [PATCH 5/6] Share httpClient with authenticator; Fix & re-enable tests Fixes https://github.com/cyberark/conjur-api-dotnet/issues/45 --- .gitleaksignore | 1 + CHANGELOG.md | 6 ++ conjur-api/ApiKeyAuthenticator.cs | 8 +- conjur-api/Client.cs | 3 +- test/AuthenticatorTest.cs | 162 +++++++++++++++--------------- test/ClientTest.cs | 59 ++++++----- test/HostFactoryTest.cs | 1 - 7 files changed, 121 insertions(+), 119 deletions(-) create mode 100644 .gitleaksignore diff --git a/.gitleaksignore b/.gitleaksignore new file mode 100644 index 0000000..188981f --- /dev/null +++ b/.gitleaksignore @@ -0,0 +1 @@ +test/HostFactoryTest.cs:generic-api-key:21 diff --git a/CHANGELOG.md b/CHANGELOG.md index 5577b62..fae2e74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +## [3.0.3] - 2024-11-22 + +### Fixed +- Resolve build warnings +- Fix and re-enable broken tests (CNJR-7227) + ## [3.0.2] - 2024-08-02 ### Changed diff --git a/conjur-api/ApiKeyAuthenticator.cs b/conjur-api/ApiKeyAuthenticator.cs index f52b4c3..342236b 100644 --- a/conjur-api/ApiKeyAuthenticator.cs +++ b/conjur-api/ApiKeyAuthenticator.cs @@ -26,6 +26,7 @@ public class ApiKeyAuthenticator : IAuthenticator private string token = null; private Timer timer = null; + private HttpClient httpClient; /// /// Initializes a new instance of the class. @@ -35,10 +36,11 @@ public class ApiKeyAuthenticator : IAuthenticator /// The name of the Conjur organization account. /// User name and API key to use, where /// username is for example "bob" or "host/jenkins". - public ApiKeyAuthenticator(Uri authnUri, string account, NetworkCredential credential) + public ApiKeyAuthenticator(Uri authnUri, string account, NetworkCredential credential, HttpClient httpClient = null) { this.credential = credential; this.uri = new Uri($"{authnUri}/{Uri.EscapeDataString(account)}/{Uri.EscapeDataString(credential.UserName)}/authenticate"); + this.httpClient = httpClient ?? new HttpClient(); } #region IAuthenticator implementation @@ -60,8 +62,6 @@ public string GetToken() { if (this.token == null) { - HttpClient httpClient = new HttpClient(); - httpClient.Timeout = TimeSpan.FromMilliseconds(ApiConfigurationManager.GetInstance().HttpRequestTimeout); HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, this.uri); IntPtr bstr = IntPtr.Zero; @@ -83,7 +83,7 @@ public string GetToken() stream.Headers.ContentLength = credential.SecurePassword.Length; httpRequestMessage.Content = stream; - var response = httpClient.Send(httpRequestMessage); + var response = this.httpClient.Send(httpRequestMessage); response.EnsureSuccessStatusCode(); Interlocked.Exchange(ref this.token, response.Read()); diff --git a/conjur-api/Client.cs b/conjur-api/Client.cs index 4f9203f..f2cb545 100644 --- a/conjur-api/Client.cs +++ b/conjur-api/Client.cs @@ -97,7 +97,8 @@ public NetworkCredential Credential this.Authenticator = new ApiKeyAuthenticator( new Uri(this.ApplianceUri + "authn"), this.GetAccountName(), - value); + value, + this.httpClient); } } diff --git a/test/AuthenticatorTest.cs b/test/AuthenticatorTest.cs index 9eb7272..eab2e8e 100644 --- a/test/AuthenticatorTest.cs +++ b/test/AuthenticatorTest.cs @@ -2,100 +2,98 @@ using System.Net; using NUnit.Framework; using System.Threading; +using System.Net.Http; namespace Conjur.Test { public class AuthenticatorTest : Base { + // file deepcode ignore NoHardcodedCredentials/test: This is a test file protected static readonly NetworkCredential credential = new NetworkCredential("username", "api-key"); protected ApiKeyAuthenticator Authenticator; - + [SetUp] public void CreateAuthenticator() { - Authenticator = new ApiKeyAuthenticator(new Uri("test:///authn"), TestAccount, credential); + Authenticator = new ApiKeyAuthenticator(new Uri("test:///authn"), TestAccount, credential, this.Client.httpClient); } - // TODO: These tests have been commented out and will be fixed in Issue #45 (https://github.com/cyberark/conjur-api-dotnet/issues/45) - - //[Test] - //public void TestTokenCaching() - //{ - // MockToken("token1"); - // Assert.AreEqual("token1", Authenticator.GetToken()); - // MockToken("token2"); - - // Assert.AreEqual("token1", Authenticator.GetToken()); - // MockTokenExpiration (); - // Assert.AreEqual("token2", Authenticator.GetToken()); - //} - - // [Test] - // public void TestTokenThreadSafe() - // { - // int authenticationCount = 0; - // Action verifier = (WebRequest wr) => - // { - // ApiKeyVerifier(wr); - // Thread.Sleep (10); - // Interlocked.Increment(ref authenticationCount); - // }; - - // string token = "token1"; - - // MockToken(token).Verifier = verifier; - - // Assert.AreEqual(token, Authenticator.GetToken()); - // Assert.AreEqual(1, authenticationCount); - - // ThreadStart checker = () => - // { - // Assert.AreEqual(token, Authenticator.GetToken()); - // }; - - // Thread t1 = new Thread(checker); - // Thread t2 = new Thread(checker); - - // t1.Start(); t2.Start(); - // t1.Join(); t2.Join(); - - // Assert.AreEqual(1, authenticationCount); - - // MockTokenExpiration(); - - // token = "token2"; - // MockToken(token).Verifier = verifier; - - // t1 = new Thread(checker); - // t2 = new Thread(checker); - - // Assert.AreEqual(1, authenticationCount); - // t1.Start(); t2.Start(); - // t1.Join(); t2.Join(); - // Assert.AreEqual(2, authenticationCount); - // } - - // static protected readonly Action ApiKeyVerifier = (WebRequest wr) => - // { - // var req = wr as WebMocker.MockRequest; - // Assert.AreEqual("POST", wr.Method); - // Assert.AreEqual("api-key", req.Body); - // }; - - - // protected WebMocker.MockRequest MockToken(string token) - // { - // var mock = Mocker.Mock(new Uri($"test:///authn/{TestAccount}/username/authenticate"), token); - // mock.Verifier = ApiKeyVerifier; - // Console.WriteLine (mock.Verifier); - // return mock; - // } - - // protected void MockTokenExpiration() - // { - // Authenticator.StartTokenTimer(new TimeSpan(0, 0, 0, 0, 1)); - // Thread.Sleep(10); - // } + [Test] + public void TestTokenCaching() + { + MockToken("token1"); + Assert.AreEqual("token1", Authenticator.GetToken()); + MockToken("token2"); + + Assert.AreEqual("token1", Authenticator.GetToken()); + MockTokenExpiration(); + Assert.AreEqual("token2", Authenticator.GetToken()); + } + + [Test] + public void TestTokenThreadSafe() + { + int authenticationCount = 0; + Action verifier = (HttpRequestMessage requestMessage) => + { + ApiKeyVerifier(requestMessage); + Thread.Sleep(10); + Interlocked.Increment(ref authenticationCount); + }; + + string token = "token1"; + + MockToken(token).Verifier = verifier; + + Assert.AreEqual(token, Authenticator.GetToken()); + Assert.AreEqual(1, authenticationCount); + + ThreadStart checker = () => + { + Assert.AreEqual(token, Authenticator.GetToken()); + }; + + Thread t1 = new Thread(checker); + Thread t2 = new Thread(checker); + + t1.Start(); t2.Start(); + t1.Join(); t2.Join(); + + Assert.AreEqual(1, authenticationCount); + + MockTokenExpiration(); + + token = "token2"; + MockToken(token).Verifier = verifier; + + t1 = new Thread(checker); + t2 = new Thread(checker); + + Assert.AreEqual(1, authenticationCount); + t1.Start(); t2.Start(); + t1.Join(); t2.Join(); + Assert.AreEqual(2, authenticationCount); + } + + static protected readonly Action ApiKeyVerifier = (HttpRequestMessage requestMessage) => + { + Assert.AreEqual(HttpMethod.Post, requestMessage.Method); + Assert.AreEqual("api-key", requestMessage.Content.ReadAsStringAsync().Result); + }; + + + protected WebMocker.MockResponse MockToken(string token) + { + var mock = Mocker.Mock(new Uri($"test:///authn/{TestAccount}/username/authenticate"), token); + mock.Verifier = ApiKeyVerifier; + return mock; + } + + protected void MockTokenExpiration() + { + Authenticator.StartTokenTimer(new TimeSpan(0, 0, 0, 0, 1)); + Thread.Sleep(10); + } } } diff --git a/test/ClientTest.cs b/test/ClientTest.cs index cea7f1e..04c3b31 100644 --- a/test/ClientTest.cs +++ b/test/ClientTest.cs @@ -3,13 +3,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; -using System.Net; -using System.Text; +using System.Net.Http; namespace Conjur.Test { public class ClientTest : Base - { + { [Test] public void TestInfo() @@ -17,30 +16,28 @@ public void TestInfo() Assert.AreEqual(TestAccount, Client.GetAccountName()); } - // TODO: This test has been commented out and will be fixed in Issue #45 (https://github.com/cyberark/conjur-api-dotnet/issues/45) - //[Test] - //public void TestLogin() - //{ - // Mocker.Mock(new Uri("test:///authn/" + TestAccount + "/login"), "api-key").Verifier = - // (WebRequest wr) => - // Assert.AreEqual("Basic YWRtaW46c2VjcmV0", wr.Headers["Authorization"]); - - // var apiKey = Client.LogIn("admin", "secret"); - // Assert.AreEqual("api-key", apiKey); - // VerifyAuthenticator (Client.Authenticator); - //} - - //private void VerifyAuthenticator(IAuthenticator authenticator) - //{ - // Mocker.Mock(new Uri("test:///authn/" + TestAccount + "/" + LoginName + "/authenticate"), "token") - // .Verifier = (WebRequest wr) => - // { - // var req = wr as WebMocker.MockRequest; - // Assert.AreEqual("POST", wr.Method); - // Assert.AreEqual("api-key", req.Body); - // }; - // Assert.AreEqual("token", authenticator.GetToken()); - //} + [Test] + public void TestLogin() + { + Mocker.Mock(new Uri("test:///authn/" + TestAccount + "/login"), "api-key").Verifier = + (HttpRequestMessage requestMessage) => + Assert.AreEqual("Basic YWRtaW46c2VjcmV0", requestMessage.Headers.GetValues("Authorization").SingleOrDefault()); + + var apiKey = Client.LogIn("admin", "secret"); + Assert.AreEqual("api-key", apiKey); + VerifyAuthenticator(Client.Authenticator); + } + + private void VerifyAuthenticator(IAuthenticator authenticator) + { + Mocker.Mock(new Uri("test:///authn/" + TestAccount + "/" + LoginName + "/authenticate"), "token") + .Verifier = (HttpRequestMessage requestMessage) => + { + Assert.AreEqual(HttpMethod.Post, requestMessage.Method); + Assert.AreEqual("api-key", requestMessage.Content.ReadAsStringAsync().Result); + }; + Assert.AreEqual("token", authenticator.GetToken()); + } [Test] public void TestAuthenticatedRequest() @@ -68,7 +65,7 @@ public void ActingAsTest() Client.Authenticator = new MockAuthenticator(); - IEnumerator actingAsClientVars = Client.ActingAs(role).ListVariables(null,1000,0).GetEnumerator(); + IEnumerator actingAsClientVars = Client.ActingAs(role).ListVariables(null, 1000, 0).GetEnumerator(); IEnumerator plainClientVars = Client.ListVariables(null, 1000, 0).GetEnumerator(); Assert.AreEqual(true, actingAsClientVars.MoveNext()); @@ -102,10 +99,10 @@ public void CreatePolicyTest() { StreamReader reader = new StreamReader(policyResStream); Assert.AreEqual(policyResponseText, reader.ReadToEnd()); - } - catch + } + catch { - Assert.Fail("Failure in policy load response"); + Assert.Fail("Failure in policy load response"); } } } diff --git a/test/HostFactoryTest.cs b/test/HostFactoryTest.cs index c493568..dbf23dd 100644 --- a/test/HostFactoryTest.cs +++ b/test/HostFactoryTest.cs @@ -23,7 +23,6 @@ public void TestCreateHost() .Verifier = (HttpRequestMessage requestMessage) => { Assert.AreEqual(HttpMethod.Post, requestMessage.Method); - Console.WriteLine(requestMessage.Headers.GetValues("Authorization").SingleOrDefault()); if (requestMessage.Headers.GetValues("Authorization").SingleOrDefault() != "Token token=\"host-factory-token\"") throw new UnauthorizedException("Unauthorized", new HttpRequestException("Unauthorized")); }; From f2eadde5afd8285d54389cc461012c90a0ba2ef9 Mon Sep 17 00:00:00 2001 From: Shlomo Zalman Heigh Date: Fri, 22 Nov 2024 10:25:08 -0500 Subject: [PATCH 6/6] Style fixes --- conjur-api/ApiKeyAuthenticator.cs | 3 +-- test/AuthenticatorTest.cs | 2 +- test/HostFactoryTest.cs | 2 ++ 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conjur-api/ApiKeyAuthenticator.cs b/conjur-api/ApiKeyAuthenticator.cs index 342236b..1fde04b 100644 --- a/conjur-api/ApiKeyAuthenticator.cs +++ b/conjur-api/ApiKeyAuthenticator.cs @@ -9,7 +9,6 @@ using System.IO; using System.Net; using System.Net.Http; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; @@ -26,7 +25,7 @@ public class ApiKeyAuthenticator : IAuthenticator private string token = null; private Timer timer = null; - private HttpClient httpClient; + private readonly HttpClient httpClient; /// /// Initializes a new instance of the class. diff --git a/test/AuthenticatorTest.cs b/test/AuthenticatorTest.cs index eab2e8e..5fc3e22 100644 --- a/test/AuthenticatorTest.cs +++ b/test/AuthenticatorTest.cs @@ -35,7 +35,7 @@ public void TestTokenCaching() public void TestTokenThreadSafe() { int authenticationCount = 0; - Action verifier = (HttpRequestMessage requestMessage) => + var verifier = (HttpRequestMessage requestMessage) => { ApiKeyVerifier(requestMessage); Thread.Sleep(10); diff --git a/test/HostFactoryTest.cs b/test/HostFactoryTest.cs index dbf23dd..b3a6823 100644 --- a/test/HostFactoryTest.cs +++ b/test/HostFactoryTest.cs @@ -24,7 +24,9 @@ public void TestCreateHost() { Assert.AreEqual(HttpMethod.Post, requestMessage.Method); if (requestMessage.Headers.GetValues("Authorization").SingleOrDefault() != "Token token=\"host-factory-token\"") + { throw new UnauthorizedException("Unauthorized", new HttpRequestException("Unauthorized")); + } }; Assert.Throws(