Skip to content

Commit fc51ba5

Browse files
authored
Merge pull request #916 from dorssel/rules
Add policy rules
2 parents 46c7f5c + 42415ad commit fc51ba5

19 files changed

+866
-33
lines changed

Installer/Server.wxs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ SPDX-License-Identifier: GPL-3.0-only
7373
Key="Devices"
7474
ForceCreateOnInstall="yes"
7575
/>
76+
<RegistryKey
77+
Key="Policy"
78+
ForceCreateOnInstall="yes"
79+
/>
7680
</RegistryKey>
7781
<Environment
7882
Id="PATH"

UnitTests/BusId_Tests.cs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ sealed class BusIdData
7474
"0-0",
7575
"0-1",
7676
"1-0",
77-
"1-65536",
78-
"65536-1",
77+
"1-100",
78+
"100-1",
7979
"01-1",
8080
"1-01",
8181
];
@@ -89,20 +89,20 @@ public static IEnumerable<string[]> Invalid
8989
"IncompatibleHub",
9090
"1-1",
9191
"1-2",
92-
"1-65534",
93-
"1-65535",
92+
"1-98",
93+
"1-99",
9494
"2-1",
9595
"2-2",
96-
"2-65534",
97-
"2-65535",
98-
"65534-1",
99-
"65534-2",
100-
"65534-65534",
101-
"65534-65535",
102-
"65535-1",
103-
"65535-2",
104-
"65535-65534",
105-
"65535-65535",
96+
"2-98",
97+
"2-99",
98+
"98-1",
99+
"98-2",
100+
"98-98",
101+
"98-99",
102+
"99-1",
103+
"99-2",
104+
"99-98",
105+
"99-99",
106106
];
107107

108108
public static IEnumerable<string[]> Valid => from value in _Valid select new string[] { value };

UnitTests/Parse_policy_Tests.cs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-only
4+
5+
using System.CommandLine;
6+
using Usbipd.Automation;
7+
8+
namespace UnitTests;
9+
10+
[TestClass]
11+
sealed class Parse_policy_Tests
12+
: ParseTestBase
13+
{
14+
static readonly BusId TestBusId = BusId.Parse("3-42");
15+
static readonly Guid TestGuid = Guid.Parse("{E863A2AF-AE47-440B-A32B-FAB1C03017AB}");
16+
static readonly VidPid TestHardwareId = VidPid.Parse("0123:cdef");
17+
18+
[TestMethod]
19+
public void Help()
20+
{
21+
Test(ExitCode.Success, "policy", "--help");
22+
}
23+
24+
[TestMethod]
25+
public void CommandMissing()
26+
{
27+
Test(ExitCode.ParseError, "policy");
28+
}
29+
30+
[TestMethod]
31+
public void CommandInvalid()
32+
{
33+
Test(ExitCode.ParseError, "policy", "invalid-command");
34+
}
35+
36+
[TestMethod]
37+
public void OptionInvalid()
38+
{
39+
Test(ExitCode.ParseError, "policy", "--invalid-option");
40+
}
41+
42+
[TestMethod]
43+
public void list_Success()
44+
{
45+
var mock = CreateMock();
46+
mock.Setup(m => m.PolicyList(It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
47+
48+
Test(ExitCode.Success, mock, "policy", "list");
49+
}
50+
51+
[TestMethod]
52+
public void list_Help()
53+
{
54+
Test(ExitCode.Success, "policy", "list", "--help");
55+
}
56+
57+
[TestMethod]
58+
public void list_OptionInvalid()
59+
{
60+
Test(ExitCode.ParseError, "policy", "list", "--invalid-option");
61+
}
62+
63+
[TestMethod]
64+
public void list_StrayArgument()
65+
{
66+
Test(ExitCode.ParseError, "policy", "list", "stray-argument");
67+
}
68+
69+
[TestMethod]
70+
public void add_BusIdSuccess()
71+
{
72+
var mock = CreateMock();
73+
mock.Setup(m => m.PolicyAdd(It.IsAny<PolicyRule>(),
74+
It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
75+
76+
Test(ExitCode.Success, mock, "policy", "add", "--effect", "Allow", "--operation", "AutoBind", "--busid", TestBusId.ToString());
77+
}
78+
79+
[TestMethod]
80+
public void add_HardwareIdSuccess()
81+
{
82+
var mock = CreateMock();
83+
mock.Setup(m => m.PolicyAdd(It.IsAny<PolicyRule>(),
84+
It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
85+
86+
Test(ExitCode.Success, mock, "policy", "add", "--effect", "Allow", "--operation", "AutoBind", "--hardware-id", TestHardwareId.ToString());
87+
}
88+
89+
[TestMethod]
90+
public void add_BothSuccess()
91+
{
92+
var mock = CreateMock();
93+
mock.Setup(m => m.PolicyAdd(It.IsAny<PolicyRule>(),
94+
It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
95+
96+
Test(ExitCode.Success, mock, "policy", "add", "--effect", "Allow", "--operation", "AutoBind",
97+
"--busid", TestBusId.ToString(), "--hardware-id", TestHardwareId.ToString());
98+
}
99+
100+
[TestMethod]
101+
public void add_MissingCondition()
102+
{
103+
Test(ExitCode.ParseError, "policy", "add", "--effect", "Allow", "--operation", "AutoBind", TestHardwareId.ToString());
104+
}
105+
106+
[TestMethod]
107+
public void remove_GuidSuccess()
108+
{
109+
var mock = CreateMock();
110+
mock.Setup(m => m.PolicyRemove(It.IsAny<Guid>(),
111+
It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
112+
113+
Test(ExitCode.Success, mock, "policy", "remove", "--guid", TestGuid.ToString());
114+
}
115+
116+
[TestMethod]
117+
public void remove_AllSuccess()
118+
{
119+
var mock = CreateMock();
120+
mock.Setup(m => m.PolicyRemoveAll(It.IsNotNull<IConsole>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult(ExitCode.Success));
121+
122+
Test(ExitCode.Success, mock, "policy", "remove", "--all");
123+
}
124+
125+
[TestMethod]
126+
public void remove_MissingOption()
127+
{
128+
Test(ExitCode.ParseError, "policy", "remove");
129+
}
130+
131+
[TestMethod]
132+
public void remove_TooManyOptions()
133+
{
134+
Test(ExitCode.ParseError, "policy", "remove", "--all", "--guid", TestGuid.ToString());
135+
}
136+
}

UnitTests/Parse_usbipd_Tests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ sealed class Parse_usbipd_Tests
99
: ParseTestBase
1010
{
1111
[TestMethod]
12-
public void Success()
12+
public void NoCommand()
1313
{
14-
Test(ExitCode.Success);
14+
Test(ExitCode.ParseError);
1515
}
1616

1717
[TestMethod]

UnitTests/Policy_Tests.cs

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-only
4+
5+
using Microsoft.Win32;
6+
using Usbipd.Automation;
7+
8+
namespace UnitTests;
9+
10+
[TestClass]
11+
sealed class Policy_Tests
12+
{
13+
static readonly BusId TestBusId = BusId.Parse("3-42");
14+
static readonly VidPid TestHardwareId = VidPid.Parse("0123:cdef");
15+
static readonly BusId OtherBusId = BusId.Parse("1-1");
16+
static readonly VidPid OtherHardwareId = VidPid.Parse("4567:89ab");
17+
18+
static TempRegistry BaseTempRegistryKey = null!;
19+
20+
[ClassInitialize]
21+
public static void ClassInitialize(TestContext testContext)
22+
{
23+
_ = testContext;
24+
25+
BaseTempRegistryKey = new TempRegistry(Registry.CurrentUser);
26+
}
27+
28+
[ClassCleanup]
29+
public static void ClassCleanup()
30+
{
31+
BaseTempRegistryKey.Dispose();
32+
}
33+
34+
static TempRegistry CreateTempRegistry() => new(BaseTempRegistryKey.Key);
35+
36+
[TestMethod]
37+
public void Constructor_Success()
38+
{
39+
_ = new PolicyRuleAutoBind(PolicyRuleEffect.Allow, TestBusId, TestHardwareId);
40+
}
41+
42+
sealed class AutoBindData
43+
{
44+
static readonly PolicyRuleAutoBind[] _Valid = [
45+
new(PolicyRuleEffect.Allow, TestBusId, null),
46+
new(PolicyRuleEffect.Allow, null, TestHardwareId),
47+
new(PolicyRuleEffect.Allow, TestBusId, TestHardwareId),
48+
];
49+
50+
static readonly PolicyRuleAutoBind[] _Invalid = [
51+
new(PolicyRuleEffect.Allow, null, null),
52+
new(PolicyRuleEffect.Allow, BusId.IncompatibleHub, null),
53+
new(PolicyRuleEffect.Allow, BusId.IncompatibleHub, TestHardwareId),
54+
];
55+
56+
public static IEnumerable<PolicyRuleAutoBind[]> Valid
57+
=> from value in _Valid select new PolicyRuleAutoBind[] { value };
58+
59+
public static IEnumerable<PolicyRuleAutoBind[]> Invalid
60+
=> from value in _Invalid select new PolicyRuleAutoBind[] { value };
61+
}
62+
63+
[TestMethod]
64+
[DynamicData(nameof(AutoBindData.Valid), typeof(AutoBindData))]
65+
public void Persist_AutoBind(PolicyRuleAutoBind rule)
66+
{
67+
using var tempRegistry = CreateTempRegistry();
68+
rule.Save(tempRegistry.Key);
69+
70+
var verify = PolicyRuleAutoBind.Load(rule.Effect, tempRegistry.Key);
71+
72+
Assert.AreEqual(rule.BusId, verify.BusId);
73+
Assert.AreEqual(rule.HardwareId, verify.HardwareId);
74+
}
75+
76+
[TestMethod]
77+
[DynamicData(nameof(AutoBindData.Valid), typeof(AutoBindData))]
78+
public void Valid_AutoBind(PolicyRuleAutoBind rule)
79+
{
80+
Assert.IsTrue(rule.IsValid());
81+
}
82+
83+
[TestMethod]
84+
[DynamicData(nameof(AutoBindData.Invalid), typeof(AutoBindData))]
85+
public void Invalid_AutoBind(PolicyRuleAutoBind rule)
86+
{
87+
Assert.IsFalse(rule.IsValid());
88+
}
89+
90+
static UsbDevice CreateTestUsbDevice(BusId busId, VidPid hardwareId)
91+
{
92+
return new UsbDevice($"VID_{hardwareId.Vid:X04}&PID_{hardwareId.Pid:X04}", "Description", false, busId, null, null, null);
93+
}
94+
95+
[TestMethod]
96+
[DynamicData(nameof(AutoBindData.Invalid), typeof(AutoBindData))]
97+
public void Match_Invalid_AutoBind(PolicyRuleAutoBind rule)
98+
{
99+
Assert.ThrowsException<InvalidOperationException>(() =>
100+
{
101+
rule.Matches(CreateTestUsbDevice(TestBusId, TestHardwareId));
102+
});
103+
}
104+
105+
[TestMethod]
106+
[DynamicData(nameof(AutoBindData.Valid), typeof(AutoBindData))]
107+
public void Match_Valid_AutoBind(PolicyRuleAutoBind rule)
108+
{
109+
Assert.IsTrue(rule.Matches(CreateTestUsbDevice(TestBusId, TestHardwareId)));
110+
Assert.IsFalse(rule.Matches(CreateTestUsbDevice(OtherBusId, OtherHardwareId)));
111+
if (rule.BusId is null)
112+
{
113+
Assert.IsTrue(rule.Matches(CreateTestUsbDevice(OtherBusId, TestHardwareId)));
114+
}
115+
else
116+
{
117+
Assert.IsFalse(rule.Matches(CreateTestUsbDevice(OtherBusId, TestHardwareId)));
118+
}
119+
if (rule.HardwareId is null)
120+
{
121+
Assert.IsTrue(rule.Matches(CreateTestUsbDevice(TestBusId, OtherHardwareId)));
122+
}
123+
else
124+
{
125+
Assert.IsFalse(rule.Matches(CreateTestUsbDevice(TestBusId, OtherHardwareId)));
126+
}
127+
}
128+
}

UnitTests/TempRegistry.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-FileCopyrightText: 2024 Frans van Dorsselaer
2+
//
3+
// SPDX-License-Identifier: GPL-3.0-only
4+
5+
using Microsoft.Win32;
6+
7+
namespace UnitTests;
8+
9+
sealed class TempRegistry : IDisposable
10+
{
11+
public string Name { get; } = Guid.NewGuid().ToString("B");
12+
public RegistryKey Key { get; }
13+
public RegistryKey Parent { get; }
14+
15+
public TempRegistry(RegistryKey parent)
16+
{
17+
Parent = parent;
18+
Key = Parent.CreateSubKey(Name, true, RegistryOptions.Volatile);
19+
}
20+
21+
public void Dispose()
22+
{
23+
Key.Close();
24+
Parent.DeleteSubKeyTree(Name, false);
25+
}
26+
}

Usbipd.Automation/BusId.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,13 @@ public BusId(ushort bus, ushort port)
2222
{
2323
// Do not allow the explicit creation of the special IncompatibleHub value.
2424
// Instead, use the static IncompatibleHub field (preferrable) or "default".
25-
if (bus == 0)
25+
// USB supports up to 127 devices, but that would require multiple hubs; the "per hub" port will never be >99.
26+
// And if you have more than 99 hubs on one system, then you win a prize! (but we're not going to support it...)
27+
if (bus == 0 || bus > 99)
2628
{
2729
throw new ArgumentOutOfRangeException(nameof(bus));
2830
}
29-
if (port == 0)
31+
if (port == 0 || port > 99)
3032
{
3133
throw new ArgumentOutOfRangeException(nameof(port));
3234
}
@@ -63,8 +65,8 @@ public static bool TryParse(string input, out BusId busId)
6365
}
6466
var match = Regex.Match(input, "^([1-9][0-9]*)-([1-9][0-9]*)$");
6567
if (match.Success
66-
&& ushort.TryParse(match.Groups[1].Value, out var bus)
67-
&& ushort.TryParse(match.Groups[2].Value, out var port))
68+
&& ushort.TryParse(match.Groups[1].Value, out var bus) && bus <= 99
69+
&& ushort.TryParse(match.Groups[2].Value, out var port) && port <= 99)
6870
{
6971
busId = new(bus, port);
7072
return true;

0 commit comments

Comments
 (0)