Skip to content

Commit 99a6e5b

Browse files
committed
[dotnet] Fix Virtual Authenticator removal, annotate NRT
1 parent 6dc99f5 commit 99a6e5b

File tree

5 files changed

+200
-91
lines changed

5 files changed

+200
-91
lines changed

dotnet/src/webdriver/VirtualAuth/Credential.cs

Lines changed: 37 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,31 +18,30 @@
1818
// </copyright>
1919

2020
using OpenQA.Selenium.Internal;
21+
using System;
2122
using System.Collections.Generic;
2223

24+
#nullable enable
25+
2326
namespace OpenQA.Selenium.VirtualAuth
2427
{
2528
/// <summary>
2629
/// A credential stored in a virtual authenticator.
27-
/// Refer https://w3c.github.io/webauthn/#credential-parameters
30+
/// Refer <see href="https://w3c.github.io/webauthn/#credential-parameters"/>
2831
/// </summary>
29-
public class Credential
32+
public sealed class Credential
3033
{
3134
private readonly byte[] id;
32-
private readonly bool isResidentCredential;
33-
private readonly string rpId;
34-
private readonly string privateKey;
35-
private readonly byte[] userHandle;
36-
private readonly int signCount;
35+
private readonly byte[]? userHandle;
3736

38-
private Credential(byte[] id, bool isResidentCredential, string rpId, string privateKey, byte[] userHandle, int signCount)
37+
private Credential(byte[] id, bool isResidentCredential, string? rpId, string privateKey, byte[]? userHandle, int signCount)
3938
{
40-
this.id = id;
41-
this.isResidentCredential = isResidentCredential;
42-
this.rpId = rpId;
43-
this.privateKey = privateKey;
39+
this.id = id ?? throw new ArgumentNullException(nameof(id));
40+
this.IsResidentCredential = isResidentCredential;
41+
this.RpId = rpId;
42+
this.PrivateKey = privateKey ?? throw new ArgumentNullException(nameof(privateKey));
4443
this.userHandle = userHandle;
45-
this.signCount = signCount;
44+
this.SignCount = signCount;
4645
}
4746

4847
/// <summary>
@@ -53,6 +52,7 @@ private Credential(byte[] id, bool isResidentCredential, string rpId, string pri
5352
/// <param name="privateKey">The private Key for the credentials.</param>
5453
/// <param name="signCount">The signature counter for the credentials.</param>
5554
/// <returns>The created instance of the Credential class.</returns>
55+
/// <exception cref="ArgumentNullException">If <paramref name="id"/> or <paramref name="privateKey"/> are <see langword="null"/>.</exception>
5656
public static Credential CreateNonResidentCredential(byte[] id, string rpId, string privateKey, int signCount)
5757
{
5858
return new Credential(id, false, rpId, privateKey, null, signCount);
@@ -67,6 +67,7 @@ public static Credential CreateNonResidentCredential(byte[] id, string rpId, str
6767
/// <param name="userHandle">The user handle associated to the credential.</param>
6868
/// <param name="signCount">The signature counter for the credentials.</param>
6969
/// <returns>The created instance of the Credential class.</returns>
70+
/// <exception cref="ArgumentNullException">If <paramref name="id"/> or <paramref name="privateKey"/> are <see langword="null"/>.</exception>
7071
public static Credential CreateResidentCredential(byte[] id, string rpId, string privateKey, byte[] userHandle, int signCount)
7172
{
7273
return new Credential(id, true, rpId, privateKey, userHandle, signCount);
@@ -75,50 +76,32 @@ public static Credential CreateResidentCredential(byte[] id, string rpId, string
7576
/// <summary>
7677
/// Gets the byte array of the ID of the credential.
7778
/// </summary>
78-
public byte[] Id
79-
{
80-
get { return (byte[])id.Clone(); }
81-
}
79+
public byte[] Id => (byte[])id.Clone();
8280

8381
/// <summary>
8482
/// Gets a value indicating whether this Credential is a resident credential.
8583
/// </summary>
86-
public bool IsResidentCredential
87-
{
88-
get { return this.isResidentCredential; }
89-
}
84+
public bool IsResidentCredential { get; }
9085

9186
/// <summary>
9287
/// Gets the ID of the relying party of this credential.
9388
/// </summary>
94-
public string RpId
95-
{
96-
get { return this.rpId; }
97-
}
89+
public string? RpId { get; }
9890

9991
/// <summary>
10092
/// Gets the private key of the credential.
10193
/// </summary>
102-
public string PrivateKey
103-
{
104-
get { return this.privateKey; }
105-
}
94+
public string PrivateKey { get; }
10695

10796
/// <summary>
10897
/// Gets the user handle of the credential.
10998
/// </summary>
110-
public byte[] UserHandle
111-
{
112-
get { return userHandle == null ? null : (byte[])userHandle.Clone(); }
113-
}
99+
public byte[]? UserHandle => (byte[]?)userHandle?.Clone();
114100

115101
/// <summary>
116102
/// Gets the signature counter associated to the public key credential source.
117103
/// </summary>
118-
public int SignCount
119-
{
120-
get { return this.signCount; }
121-
}
104+
public int SignCount { get; }
122105

123106
/// <summary>
124107
/// Creates a Credential instance from a dictionary of values.
@@ -127,13 +110,14 @@ public int SignCount
127110
/// <returns>The created instance of the Credential.</returns>
128111
public static Credential FromDictionary(Dictionary<string, object> dictionary)
129112
{
130-
return new Credential(
131-
Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]),
132-
(bool)dictionary["isResidentCredential"],
133-
dictionary.ContainsKey("rpId") ? (string)dictionary["rpId"] : null,
134-
(string)dictionary["privateKey"],
135-
dictionary.ContainsKey("userHandle") ? Base64UrlEncoder.DecodeBytes((string)dictionary["userHandle"]) : null,
136-
(int)((long)dictionary["signCount"]));
113+
byte[] id = Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]);
114+
bool isResidentCredential = (bool)dictionary["isResidentCredential"];
115+
string? rpId = dictionary.TryGetValue("rpId", out object? r) ? (string)r : null;
116+
string privateKey = (string)dictionary["privateKey"];
117+
byte[]? userHandle = dictionary.TryGetValue("userHandle", out object? u) ? Base64UrlEncoder.DecodeBytes((string)u) : null;
118+
int signCount = (int)(long)dictionary["signCount"];
119+
120+
return new Credential(id, isResidentCredential, rpId, privateKey, userHandle, signCount);
137121
}
138122

139123
/// <summary>
@@ -145,11 +129,15 @@ public Dictionary<string, object> ToDictionary()
145129
Dictionary<string, object> toReturn = new Dictionary<string, object>();
146130

147131
toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id);
148-
toReturn["isResidentCredential"] = this.isResidentCredential;
149-
toReturn["rpId"] = this.rpId;
150-
toReturn["privateKey"] = this.privateKey;
151-
toReturn["signCount"] = this.signCount;
152-
if (this.userHandle != null)
132+
toReturn["isResidentCredential"] = this.IsResidentCredential;
133+
if (this.RpId is not null)
134+
{
135+
toReturn["rpId"] = this.RpId;
136+
}
137+
138+
toReturn["privateKey"] = this.PrivateKey;
139+
toReturn["signCount"] = this.SignCount;
140+
if (this.userHandle is not null)
153141
{
154142
toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle);
155143
}

dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
// under the License.
1818
// </copyright>
1919

20+
using System;
2021
using System.Collections.Generic;
2122

23+
#nullable enable
24+
2225
namespace OpenQA.Selenium.VirtualAuth
2326
{
2427
/// <summary>
@@ -31,18 +34,23 @@ public interface IHasVirtualAuthenticator
3134
/// </summary>
3235
/// <param name="options">The VirtualAuthenticatorOptions to use in creating the authenticator.</param>
3336
/// <returns>The ID of the added virtual authenticator.</returns>
37+
/// <exception cref="ArgumentNullException">If <paramref name="options"/> is <see langword="null"/>.</exception>
3438
string AddVirtualAuthenticator(VirtualAuthenticatorOptions options);
3539

3640
/// <summary>
3741
/// Removes a virtual authenticator.
3842
/// </summary>
3943
/// <param name="id">The ID of the virtual authenticator to remove.</param>
44+
/// <exception cref="ArgumentNullException">If <paramref name="id"/> is <see langword="null"/>.</exception>
45+
/// <exception cref="WebDriverArgumentException">If the specified virtual authenticator does not exist.</exception>
4046
void RemoveVirtualAuthenticator(string id);
4147

4248
/// <summary>
4349
/// Adds a credential to the virtual authenticator.
4450
/// </summary>
4551
/// <param name="credential">The credential to add to the authenticator.</param>
52+
/// <exception cref="ArgumentNullException">If <paramref name="credential"/> is <see langword="null"/>.</exception>
53+
/// <exception cref="InvalidOperationException">If a Virtual Authenticator has not been added yet.</exception>
4654
void AddCredential(Credential credential);
4755

4856
/// <summary>
@@ -55,23 +63,29 @@ public interface IHasVirtualAuthenticator
5563
/// Removes a credential from the virtual authenticator.
5664
/// </summary>
5765
/// <param name="credentialId">A byte array representing the ID of the credential to remove.</param>
66+
/// <exception cref="ArgumentNullException">If <paramref name="credentialId"/> is <see langword="null"/>.</exception>
67+
/// <exception cref="InvalidOperationException">If a Virtual Authenticator has not been added yet.</exception>
5868
void RemoveCredential(byte[] credentialId);
5969

6070
/// <summary>
6171
/// Removes a credential from the virtual authenticator.
6272
/// </summary>
6373
/// <param name="credentialId">A string representing the ID of the credential to remove.</param>
74+
/// <exception cref="ArgumentNullException">If <paramref name="credentialId"/> is <see langword="null"/>.</exception>
75+
/// <exception cref="InvalidOperationException">If a Virtual Authenticator has not been added yet.</exception>
6476
void RemoveCredential(string credentialId);
6577

6678
/// <summary>
6779
/// Removes all credentials registered to this virtual authenticator.
6880
/// </summary>
81+
/// <exception cref="InvalidOperationException">If a Virtual Authenticator has not been added yet.</exception>
6982
void RemoveAllCredentials();
7083

7184
/// <summary>
7285
/// Sets whether or not a user is verified in this virtual authenticator.
7386
/// </summary>
7487
/// <param name="verified"><see langword="true"/> if the user is verified; otherwise <see langword="false"/>.</param>
88+
/// <exception cref="InvalidOperationException">If a Virtual Authenticator has not been added yet.</exception>
7589
void SetUserVerified(bool verified);
7690
}
7791
}

dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
using System;
2121
using System.Collections.Generic;
2222

23+
#nullable enable
24+
2325
namespace OpenQA.Selenium.VirtualAuth
2426
{
2527
/// <summary>
@@ -78,10 +80,13 @@ public static class Transport
7880
private bool isUserVerified = false;
7981

8082
/// <summary>
81-
/// Sets the protocol the Virtual Authenticator speaks
83+
/// Sets the Client to Authenticator Protocol (CTAP) this <see href="https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators">Virtual Authenticator</see> speaks.
8284
/// </summary>
83-
/// <param name="protocol">Valid protocol value</param>
84-
/// <returns>VirtualAuthenticatorOptions</returns>
85+
/// <param name="protocol">The CTAP protocol identifier.</param>
86+
/// <returns>This options instance for chaining.</returns>
87+
/// <remarks>Valid protocols are available on the <see cref="Protocol"/> type.</remarks>
88+
/// <exception cref="ArgumentException">If <paramref name="protocol"/> is not a supported protocol value.</exception>
89+
/// <completionlist cref="Protocol"/>
8590
public VirtualAuthenticatorOptions SetProtocol(string protocol)
8691
{
8792
if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol))
@@ -92,15 +97,19 @@ public VirtualAuthenticatorOptions SetProtocol(string protocol)
9297
else
9398
{
9499
throw new ArgumentException("Enter a valid protocol value." +
95-
"Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols.");
100+
"Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols.");
96101
}
97102
}
98103

99104
/// <summary>
100-
/// Sets the transport authenticator needs to implement to communicate with clients
105+
/// Sets the <see href="https://www.w3.org/TR/webauthn-2/#enum-transport">Authenticator Transport</see> this <see href="https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators">Virtual Authenticator</see> needs to implement, to communicate with clients.
101106
/// </summary>
102-
/// <param name="transport">Valid transport value</param>
103-
/// <returns>VirtualAuthenticatorOptions</returns>
107+
/// <param name="transport">Valid transport value.
108+
/// </param>
109+
/// <returns>This options instance for chaining.</returns>
110+
/// <remarks>Valid protocols are available on the <see cref="Transport"/> type.</remarks>
111+
/// <exception cref="ArgumentException">If <paramref name="transport"/> is not a supported transport value.</exception>
112+
/// <completionlist cref="Transport"/>
104113
public VirtualAuthenticatorOptions SetTransport(string transport)
105114
{
106115
if (Transport.BLE == transport || Transport.INTERNAL == transport || Transport.NFC == transport || Transport.USB == transport)
@@ -111,60 +120,57 @@ public VirtualAuthenticatorOptions SetTransport(string transport)
111120
else
112121
{
113122
throw new ArgumentException("Enter a valid transport value." +
114-
"Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values.");
123+
"Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values.");
115124
}
116125
}
117126

118127
/// <summary>
119-
/// If set to true the authenticator will support client-side discoverable credentials.
120-
/// Refer https://w3c.github.io/webauthn/#client-side-discoverable-credential
128+
/// If set to <see langword="true"/>, the authenticator will support <see href="https://w3c.github.io/webauthn/#client-side-discoverable-credential">Client-side discoverable Credentials</see>.
121129
/// </summary>
122-
/// <param name="hasResidentKey">boolean value to set</param>
123-
/// <returns>VirtualAuthenticatorOptions</returns>
130+
/// <param name="hasResidentKey">Whether authenticator will support client-side discoverable credentials.</param>
131+
/// <returns>This options instance for chaining.</returns>
124132
public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey)
125133
{
126134
this.hasResidentKey = hasResidentKey;
127135
return this;
128136
}
129137

130138
/// <summary>
131-
/// If set to true, the authenticator supports user verification.
132-
/// Refer https://w3c.github.io/webauthn/#user-verification.
139+
/// If set to <see langword="true"/>, the authenticator will support <see href="https://w3c.github.io/webauthn/#user-verification">User Verification</see>.
133140
/// </summary>
134-
/// <param name="hasUserVerification">boolean value to set</param>
135-
/// <returns></returns>
141+
/// <param name="hasUserVerification">Whether the authenticator supports user verification.</param>
142+
/// <returns>This options instance for chaining.</returns>
136143
public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification)
137144
{
138145
this.hasUserVerification = hasUserVerification;
139146
return this;
140147
}
141148

142149
/// <summary>
143-
/// If set to true, a user consent will always be granted.
144-
/// Refer https://w3c.github.io/webauthn/#user-consent
150+
/// If set to <see langword="true"/>, a <see href="https://w3c.github.io/webauthn/#user-consent">User Consent</see> will always be granted.
151+
/// Refer
145152
/// </summary>
146-
/// <param name="isUserConsenting">boolean value to set</param>
147-
/// <returns>VirtualAuthenticatorOptions</returns>
153+
/// <param name="isUserConsenting">Whether a user consent will always be granted.</param>
154+
/// <returns>This options instance for chaining.</returns>
148155
public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting)
149156
{
150157
this.isUserConsenting = isUserConsenting;
151158
return this;
152159
}
153160

154161
/// <summary>
155-
/// If set to true, User Verification will always succeed.
156-
/// Refer https://w3c.github.io/webauthn/#user-verification
162+
/// If set to <see langword="true"/>, <see href="https://w3c.github.io/webauthn/#user-verification">User Verification</see> will always succeed.
157163
/// </summary>
158-
/// <param name="isUserVerified">boolean value to set</param>
159-
/// <returns>VirtualAuthenticatorOptions</returns>
164+
/// <param name="isUserVerified">Whether User Verification will always succeed.</param>
165+
/// <returns>This options instance for chaining.</returns>
160166
public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified)
161167
{
162168
this.isUserVerified = isUserVerified;
163169
return this;
164170
}
165171

166172
/// <summary>
167-
/// Serializes this set of options to a dictionary of key-value pairs.
173+
/// Serializes this set of options into a dictionary of key-value pairs.
168174
/// </summary>
169175
/// <returns>The dictionary containing the values of this set of options.</returns>
170176
public Dictionary<string, object> ToDictionary()

0 commit comments

Comments
 (0)