Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make YubiKeyDeviceListener resettable #89

Merged
merged 5 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions Yubico.Core/src/Yubico/Core/Devices/DeviceEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright 2024 Yubico AB
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices
{
/// <summary>
/// Defines the contract for device-related event arguments in a generic context.
/// This interface allows for type-safe access to device information in event handling
/// scenarios, supporting device types that implement the <see cref="IDevice"/> interface.
/// It enables specific device type information to be preserved and accessed in event handlers.
/// </summary>
/// <remarks>
/// While this interface does not inherit from <see cref="System.EventArgs"/>, it retains the "Args" suffix
/// in its name. This naming convention is deliberately chosen to maintain consistency with standard
/// event argument naming patterns in C#, particularly for improved readability when used in delegate
/// and event handler signatures. The familiar "Args" suffix clearly indicates the interface's role
/// in event-related contexts, despite not directly extending <see cref="System.EventArgs"/>.
/// </remarks>
/// <typeparam name="TDevice">The specific type of <see cref="IDevice"/> this event argument represents.
/// This type parameter is covariant, allowing for more specific device types to be used
/// where a more general device type is expected.</typeparam>
#pragma warning disable CA1711 // Identifiers should not have incorrect suffix
public interface IDeviceEventArgs<out TDevice> where TDevice : IDevice
#pragma warning restore CA1711 // Identifiers should not have incorrect suffix
{
/// <summary>
/// Gets the specific type of <see cref="IDevice"/> that originated the event.
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </summary>
/// <remarks>
/// This property provides access to the specific <c>TDevice</c> instance associated with the current event.
/// </remarks>
/// <value>
/// An instance of <c>TDevice</c> that triggered this event.
/// </value>
TDevice Device { get; }
}

/// <summary>
/// Event arguments given whenever a device is added or removed from the system, providing strongly-typed access to the device that triggered the event.
/// </summary>
/// <typeparam name="TDevice">The type of device associated with this event, which must implement <see cref="IDevice"/>.</typeparam>
public abstract class DeviceEventArgs<TDevice> : EventArgs, IDeviceEventArgs<TDevice>
where TDevice : IDevice
{
/// <inheritdoc />
public TDevice Device { get; }

/// <summary>
/// Constructs a new instance of the <see cref="DeviceEventArgs{TDevice}"/> class.
/// </summary>
protected DeviceEventArgs(TDevice device)
{
Device = device;
}
}
}
16 changes: 2 additions & 14 deletions Yubico.Core/src/Yubico/Core/Devices/Hid/HidDeviceEventArgs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,22 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.Hid
{
/// <summary>
/// Event arguments given whenever a HID device is added or removed from the system.
/// </summary>
public class HidDeviceEventArgs : EventArgs
public class HidDeviceEventArgs : DeviceEventArgs<IHidDevice>
{
/// <summary>
/// The HID device that originated the event.
/// </summary>
/// <remarks>
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </remarks>
public IHidDevice? Device { get; set; }

/// <summary>
/// Constructs a new instance of the <see cref="HidDeviceEventArgs"/> class.
/// </summary>
/// <param name="device">
/// The HID device that is originating this event.
/// </param>
public HidDeviceEventArgs(IHidDevice? device)
public HidDeviceEventArgs(IHidDevice device) : base(device)
{
Device = device;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ protected void OnArrived(IHidDevice device)
/// <param name="device">
/// The device instance that originates this event.
/// </param>
protected void OnRemoved(IHidDevice? device)
protected void OnRemoved(IHidDevice device)
{
_log.LogInformation("HID {Device} removed.", device);
Removed?.Invoke(this, new HidDeviceEventArgs(device));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,6 @@ private void ArrivedCallback(IntPtr context, int result, IntPtr sender, IntPtr d
OnArrived(new MacOSHidDevice(MacOSHidDevice.GetEntryId(device)));

private void RemovedCallback(IntPtr context, int result, IntPtr sender, IntPtr device) =>
OnRemoved(null);
OnRemoved(NullDevice.Instance);
}
}
38 changes: 38 additions & 0 deletions Yubico.Core/src/Yubico/Core/Devices/Hid/NullDevice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2024 Yubico AB
//
// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.Hid
{
public class NullDevice : IHidDevice
{
public string Path => string.Empty;
public string? ParentDeviceId => default;
public DateTime LastAccessed => default;
public short VendorId => default;
public short ProductId => default;
public short Usage => default;
public HidUsagePage UsagePage => default;
public IHidConnection ConnectToFeatureReports() => throw new NotImplementedException();
public IHidConnection ConnectToIOReports() => throw new NotImplementedException();

/// <summary>
/// Creates a default <see cref="IHidDevice"/> with all it's properties set to its default values, indicating a null device.
/// This might be used in cases where the <see cref="IHidDevice"/> otherwise would be null.
/// </summary>
internal static IHidDevice Instance => new NullDevice();
private NullDevice() { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ private static int OnEventReceived(IntPtr hNotify, IntPtr context, CM_NOTIFY_ACT
}
else if (action == CM_NOTIFY_ACTION.DEVICEINTERFACEREMOVAL)
{
thisObj?.OnRemoved(null);
thisObj?.OnRemoved(NullDevice.Instance);
}

return 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System;

namespace Yubico.Core.Devices.SmartCard
{
/// <summary>
/// Event arguments given whenever a smart card device is added or removed from the system.
/// </summary>
public class SmartCardDeviceEventArgs : EventArgs
public class SmartCardDeviceEventArgs : DeviceEventArgs<ISmartCardDevice>
{
/// <summary>
/// The smart card device that originated the event.
/// </summary>
/// <remarks>
/// This property will always be populated, regardless of whether this is an arrival event or a removal event.
/// If the device was removed, not all members will be available on the object. An exception will be thrown if
/// you try to use the device in a way that requires it to be present.
/// </remarks>
public ISmartCardDevice Device { get; set; }

/// <summary>
/// Constructs a new instance of the <see cref="SmartCardDeviceEventArgs"/> class.
/// </summary>
/// <param name="device">
/// The smart card device that is originating this event.
/// </param>
public SmartCardDeviceEventArgs(ISmartCardDevice device)
public SmartCardDeviceEventArgs(ISmartCardDevice device) : base(device)
{
Device = device;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ public void TestSpecializedKeyboardSupportsModhexString(KeyboardLayout layout)
#if Windows
#pragma warning disable CA1825
[Theory]
#pragma warning disable CA1825 // Avoid zero-length array allocations
// The compiler mistakenly thinks that this somehow involves a zero-length array.
[MemberData(nameof(GetTestData))]
#pragma warning restore CA1825 // Avoid zero-length array allocations
public void GetChar_GivenHidCode_ReturnsCorrectChar(KeyboardLayout layout, (char, byte)[] testData)
{
var hid = HidCodeTranslator.GetInstance(layout);
Expand Down
5 changes: 5 additions & 0 deletions Yubico.YubiKey/src/Yubico/YubiKey/YubiKeyDevice.Static.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ public partial class YubiKeyDevice
/// <see cref="IYubiKeyDevice"/> using their serial number. If they cannot be matched,
/// each connection will be returned as a separate <see cref="IYubiKeyDevice"/>.
/// </para>
/// <para>
/// If your application no longer needs to watch for insertion or removal notifications,
/// you can call <see cref="YubiKeyDeviceListener.StopListening"/> to release resources
/// and avoid the logging and other actions from the listeners.
/// </para>
/// </remarks>
/// <param name="transport">
/// Argument controls which devices are searched for. Values <see cref="Transport.None"/>
Expand Down
Loading
Loading