Skip to content

Commit

Permalink
Part of SLVS-1732
Browse files Browse the repository at this point in the history
Refactor ConnectionInformation
  • Loading branch information
gabriela-trutan-sonarsource committed Dec 23, 2024
1 parent 16a9f7e commit 15efd71
Show file tree
Hide file tree
Showing 19 changed files with 288 additions and 167 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ await sonarQubeService
.Received()
.ConnectAsync(
Arg.Is<ConnectionInformation>(x => x.ServerUri.Equals("https://sonarcloud.io/")
&& x.UserName.Equals(ValidToken.UserName)
&& string.IsNullOrEmpty(x.Password.ToUnsecureString())),
&& ((BasicAuthCredentials)x.Credentials).UserName.Equals(ValidToken.UserName)
&& string.IsNullOrEmpty(((BasicAuthCredentials)x.Credentials).Password.ToUnsecureString())),
ACancellationToken);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* SonarLint for Visual Studio
* Copyright (C) 2016-2024 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using SonarLint.VisualStudio.ConnectedMode.Persistence;
using SonarLint.VisualStudio.TestInfrastructure;
using SonarQube.Client.Helpers;

namespace SonarLint.VisualStudio.ConnectedMode.UnitTests.Persistence;

[TestClass]
public class BasicAuthCredentialsTests
{
private const string Username = "username";
private const string Password = "pwd";

[TestMethod]
public void Ctor_WhenUsernameIsNull_ThrowsArgumentNullException()
{
Action act = () => new BasicAuthCredentials(null, Password.ToSecureString());

act.Should().Throw<ArgumentNullException>();
}

[TestMethod]
public void Ctor_WhenPasswordIsNull_ThrowsArgumentNullException()
{
Action act = () => new BasicAuthCredentials(Username, null);

act.Should().Throw<ArgumentNullException>();
}

[TestMethod]
public void Dispose_DisposesPassword()
{
var testSubject = new BasicAuthCredentials(Username, Password.ToSecureString());

testSubject.Dispose();

Exceptions.Expect<ObjectDisposedException>(() => testSubject.Password.ToUnsecureString());
}

[TestMethod]
public void Clone_ClonesPassword()
{
var password = "pwd";
var testSubject = new BasicAuthCredentials(Username, password.ToSecureString());

var clone = (BasicAuthCredentials)testSubject.Clone();

clone.Should().NotBeSameAs(testSubject);
clone.Password.Should().NotBeSameAs(testSubject.Password);
clone.Password.ToUnsecureString().Should().Be(testSubject.Password.ToUnsecureString());
clone.UserName.Should().Be(testSubject.UserName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using Microsoft.VisualStudio.LanguageServices.Progression;
using SonarLint.VisualStudio.ConnectedMode.Persistence;
using SonarLint.VisualStudio.Core.Binding;
using SonarLint.VisualStudio.TestInfrastructure;
Expand Down Expand Up @@ -47,8 +48,7 @@ public void BoundSonarQubeProject_CreateConnectionInformation_NoCredentials()

// Assert
conn.ServerUri.Should().Be(input.ServerUri);
conn.UserName.Should().BeNull();
conn.Password.Should().BeNull();
conn.Credentials.Should().BeAssignableTo<INoCredentials>();
conn.Organization.Key.Should().Be("org_key");
conn.Organization.Name.Should().Be("org_name");
}
Expand All @@ -66,8 +66,10 @@ public void BoundSonarQubeProject_CreateConnectionInformation_BasicAuthCredentia

// Assert
conn.ServerUri.Should().Be(input.ServerUri);
conn.UserName.Should().Be(creds.UserName);
conn.Password.ToUnsecureString().Should().Be(creds.Password.ToUnsecureString());
var basicAuth = conn.Credentials as BasicAuthCredentials;
basicAuth.Should().NotBeNull();
basicAuth.UserName.Should().Be(creds.UserName);
basicAuth.Password.ToUnsecureString().Should().Be(creds.Password.ToUnsecureString());
conn.Organization.Key.Should().Be("org_key");
conn.Organization.Name.Should().Be("org_name");
}
Expand All @@ -83,8 +85,7 @@ public void BoundSonarQubeProject_CreateConnectionInformation_NoOrganizationNoAu

// Assert
conn.ServerUri.Should().Be(input.ServerUri);
conn.UserName.Should().BeNull();
conn.Password.Should().BeNull();
conn.Credentials.Should().BeAssignableTo<INoCredentials>();
conn.Organization.Should().BeNull();
}

Expand All @@ -105,8 +106,7 @@ public void BoundServerProject_CreateConnectionInformation_NoCredentials()

// Assert
conn.ServerUri.Should().Be(input.ServerConnection.ServerUri);
conn.UserName.Should().BeNull();
conn.Password.Should().BeNull();
conn.Credentials.Should().BeAssignableTo<INoCredentials>();
conn.Organization.Key.Should().Be("org_key");
}

Expand All @@ -123,8 +123,10 @@ public void BoundServerProject_CreateConnectionInformation_BasicAuthCredentials(

// Assert
conn.ServerUri.Should().Be(input.ServerConnection.ServerUri);
conn.UserName.Should().Be(creds.UserName);
conn.Password.ToUnsecureString().Should().Be(creds.Password.ToUnsecureString());
var basicAuth = conn.Credentials as BasicAuthCredentials;
basicAuth.Should().NotBeNull();
basicAuth.UserName.Should().Be(creds.UserName);
basicAuth.Password.ToUnsecureString().Should().Be(creds.Password.ToUnsecureString());
conn.Organization.Key.Should().Be("org_key");
}

Expand All @@ -139,8 +141,7 @@ public void BoundServerProject_CreateConnectionInformation_NoOrganizationNoAuth(

// Assert
conn.ServerUri.Should().Be(input.ServerConnection.ServerUri);
conn.UserName.Should().BeNull();
conn.Password.Should().BeNull();
conn.Credentials.Should().BeAssignableTo<INoCredentials>();
conn.Organization.Should().BeNull();
}
}
3 changes: 2 additions & 1 deletion src/ConnectedMode/Binding/IUnintrusiveBindingController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System.ComponentModel.Composition;
using SonarLint.VisualStudio.Core.Binding;
using SonarQube.Client;
using SonarQube.Client.Models;
using Task = System.Threading.Tasks.Task;

namespace SonarLint.VisualStudio.ConnectedMode.Binding
Expand Down Expand Up @@ -57,7 +58,7 @@ public UnintrusiveBindingController(IBindingProcessFactory bindingProcessFactory

public async Task BindAsync(BoundServerProject project, CancellationToken cancellationToken)
{
var connectionInformation = project.ServerConnection.Credentials.CreateConnectionInformation(project.ServerConnection.ServerUri);
var connectionInformation = new ConnectionInformation(project.ServerConnection.ServerUri, project.ServerConnection.Credentials);
await sonarQubeService.ConnectAsync(connectionInformation, cancellationToken);
await BindAsync(project, null, cancellationToken);
activeSolutionChangedHandler.HandleBindingChange(false);
Expand Down
24 changes: 8 additions & 16 deletions src/ConnectedMode/Persistence/BasicAuthCredentials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using System.Security;
using SonarLint.VisualStudio.Core.Binding;
using SonarQube.Client.Helpers;
using SonarQube.Client.Models;

namespace SonarLint.VisualStudio.ConnectedMode.Persistence
namespace SonarLint.VisualStudio.ConnectedMode.Persistence;

internal sealed class BasicAuthCredentials(string userName, SecureString password) : ICredentials, IBasicAuthCredentials
{
internal class BasicAuthCredentials : ICredentials
{
public BasicAuthCredentials(string userName, SecureString password)
{
this.UserName = userName;
this.Password = password;
}
public string UserName { get; } = userName ?? throw new ArgumentNullException(nameof(userName));

public string UserName { get; }
public SecureString Password { get; } = password ?? throw new ArgumentNullException(nameof(password));

public SecureString Password { get; }
public void Dispose() => Password?.Dispose();

ConnectionInformation ICredentials.CreateConnectionInformation(Uri serverUri)
{
return new ConnectionInformation(serverUri, this.UserName, this.Password);
}
}
public object Clone() => new BasicAuthCredentials(UserName, Password.CopyAsReadOnly());
}
38 changes: 0 additions & 38 deletions src/ConnectedMode/Persistence/ConnectionInfoConverter.cs

This file was deleted.

8 changes: 2 additions & 6 deletions src/Core/Binding/BoundSonarQubeProjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ public static ConnectionInformation CreateConnectionInformation(this BoundSonarQ
throw new ArgumentNullException(nameof(binding));
}

var connection = binding.Credentials == null ?
new ConnectionInformation(binding.ServerUri)
: binding.Credentials.CreateConnectionInformation(binding.ServerUri);
var connection = new ConnectionInformation(binding.ServerUri, binding.Credentials);

connection.Organization = binding.Organization;
return connection;
Expand All @@ -47,9 +45,7 @@ public static ConnectionInformation CreateConnectionInformation(this BoundServer
throw new ArgumentNullException(nameof(binding));
}

var connection = binding.ServerConnection.Credentials == null ?
new ConnectionInformation(binding.ServerConnection.ServerUri)
: binding.ServerConnection.Credentials.CreateConnectionInformation(binding.ServerConnection.ServerUri);
var connection = new ConnectionInformation(binding.ServerConnection.ServerUri, binding.ServerConnection.Credentials);

connection.Organization = binding.ServerConnection is ServerConnection.SonarCloud sc ? new SonarQubeOrganization(sc.OrganizationKey, null) : null;
return connection;
Expand Down
4 changes: 1 addition & 3 deletions src/Core/Binding/ICredentials.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System;
using SonarQube.Client.Models;

namespace SonarLint.VisualStudio.Core.Binding
{
public interface ICredentials
public interface ICredentials : IConnectionCredentials
{
ConnectionInformation CreateConnectionInformation(Uri serverUri);
}
}
24 changes: 12 additions & 12 deletions src/Integration.UnitTests/Service/ConnectionInformationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
using System;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SonarLint.VisualStudio.ConnectedMode.Persistence;
using SonarQube.Client.Helpers;
using SonarQube.Client.Models;
using SonarLint.VisualStudio.TestInfrastructure;
Expand All @@ -38,14 +39,15 @@ public void ConnectionInformation_WithLoginInformation()
var passwordUnsecure = "admin";
var password = passwordUnsecure.ToSecureString();
var serverUri = new Uri("http://localhost/");
var testSubject = new ConnectionInformation(serverUri, userName, password);
var credentials = new BasicAuthCredentials(userName, password);
var testSubject = new ConnectionInformation(serverUri, credentials);

// Act
password.Dispose(); // Connection information should maintain it's own copy of the password

// Assert
testSubject.Password.ToUnsecureString().Should().Be(passwordUnsecure, "Password doesn't match");
testSubject.UserName.Should().Be(userName, "UserName doesn't match");
((BasicAuthCredentials)testSubject.Credentials).Password.ToUnsecureString().Should().Be(passwordUnsecure, "Password doesn't match");
((BasicAuthCredentials)testSubject.Credentials).UserName.Should().Be(userName, "UserName doesn't match");
testSubject.ServerUri.Should().Be(serverUri, "ServerUri doesn't match");

// Act clone
Expand All @@ -55,11 +57,11 @@ public void ConnectionInformation_WithLoginInformation()
testSubject.Dispose();

// Assert testSubject
Exceptions.Expect<ObjectDisposedException>(() => testSubject.Password.ToUnsecureString());
Exceptions.Expect<ObjectDisposedException>(() => ((BasicAuthCredentials)testSubject.Credentials).Password.ToUnsecureString());

// Assert testSubject2
testSubject2.Password.ToUnsecureString().Should().Be(passwordUnsecure, "Password doesn't match");
testSubject2.UserName.Should().Be(userName, "UserName doesn't match");
((BasicAuthCredentials)testSubject2.Credentials).Password.ToUnsecureString().Should().Be(passwordUnsecure, "Password doesn't match");
((BasicAuthCredentials)testSubject.Credentials).UserName.Should().Be(userName, "UserName doesn't match");
testSubject2.ServerUri.Should().Be(serverUri, "ServerUri doesn't match");
}

Expand All @@ -70,19 +72,17 @@ public void ConnectionInformation_WithoutLoginInformation()
var serverUri = new Uri("http://localhost/");

// Act
var testSubject = new ConnectionInformation(serverUri);
var testSubject = new ConnectionInformation(serverUri, null);

// Assert
testSubject.Password.Should().BeNull("Password wasn't provided");
testSubject.UserName.Should().BeNull("UserName wasn't provided");
testSubject.Credentials.Should().BeAssignableTo<INoCredentials>();
testSubject.ServerUri.Should().Be(serverUri, "ServerUri doesn't match");

// Act clone
var testSubject2 = (ConnectionInformation)((ICloneable)testSubject).Clone();

// Assert testSubject2
testSubject2.Password.Should().BeNull("Password wasn't provided");
testSubject2.UserName.Should().BeNull("UserName wasn't provided");
testSubject2.Credentials.Should().BeAssignableTo<INoCredentials>();
testSubject2.ServerUri.Should().Be(serverUri, "ServerUri doesn't match");
}

Expand Down Expand Up @@ -110,7 +110,7 @@ public void ConnectionInformation_Ctor_FixesSonarCloudUri()
public void ConnectionInformation_Ctor_ArgChecks()
{
Exceptions.Expect<ArgumentNullException>(() => new ConnectionInformation(null));
Exceptions.Expect<ArgumentNullException>(() => new ConnectionInformation(null, "user", "pwd".ToSecureString()));
Exceptions.Expect<ArgumentNullException>(() => new ConnectionInformation(null, new BasicAuthCredentials("user", "pwd".ToSecureString())));
}
}
}
Loading

0 comments on commit 15efd71

Please sign in to comment.