Skip to content

Commit 4d51d4e

Browse files
Merge pull request #12 from EvotecIT/Improve
2 parents 833de64 + c036ded commit 4d51d4e

31 files changed

+1721
-123
lines changed

Assets/README.MD

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
# LocalSecurityEditor - .NET Library
22

3-
.NET library for managing local security policy (User Rights Assignment).
3+
[![NuGet Version](https://img.shields.io/nuget/v/LocalSecurityEditor)](https://www.nuget.org/packages/LocalSecurityEditor)
4+
[![NuGet Downloads](https://img.shields.io/nuget/dt/LocalSecurityEditor)](https://www.nuget.org/packages/LocalSecurityEditor)
5+
![.NET Framework 4.7.2](https://img.shields.io/badge/.NET%20Framework-4.7.2-512BD4)
6+
![.NET Standard 2.0](https://img.shields.io/badge/.NET%20Standard-2.0-512BD4)
7+
![.NET (Windows) 8.0 | 9.0](https://img.shields.io/badge/.NET%20(Windows)-8.0%20%7C%209.0-512BD4)
8+
![Platform Windows-only](https://img.shields.io/badge/Platform-Windows--only-blue)
49

10+
.NET library for managing local security policy (User Rights Assignment). This library powers PowerShell module scenarios and general .NET automation for querying and modifying User Rights Assignments (LSA policy).
511

6-
### Supported User Rights Assignement
12+
13+
### Supported User Rights Assignment
714

815
| ConstantName | Group Policy Setting |
916
| ----------------------------------------- | ------------------------------------------------------------------ |
@@ -53,7 +60,7 @@
5360
| SeSyncAgentPrivilege | Synchronize directory service data |
5461
| SeTakeOwnershipPrivilege | Take ownership of files or other objects |
5562

56-
### Example Local Computer
63+
### Example Local Computer (LSA wrapper)
5764

5865
```csharp
5966
using System;
@@ -64,7 +71,7 @@ namespace TestApp {
6471
static void Main() {
6572
string[] accounts;
6673

67-
Console.WriteLine("[*] Accessing server - Displaying Current");
74+
Console.WriteLine("[*] Displaying current assignments (local)");
6875

6976
using (LsaWrapper lsa = new LsaWrapper()) {
7077
accounts = lsa.GetPrivileges(UserRightsAssignment.SeBatchLogonRight);
@@ -74,13 +81,13 @@ namespace TestApp {
7481
Console.WriteLine(account);
7582
}
7683

77-
Console.WriteLine("[*] Adding Account to the Server");
84+
Console.WriteLine("[*] Granting right to an account");
7885

7986
using (LsaWrapper lsa = new LsaWrapper()) {
8087
lsa.AddPrivileges("EVOTEC\\przemyslaw.klys", UserRightsAssignment.SeBatchLogonRight);
8188
}
8289

83-
Console.WriteLine("[*] Accessing server - Displaying Current");
90+
Console.WriteLine("[*] Displaying current assignments (local)");
8491

8592
using (LsaWrapper lsa = new LsaWrapper()) {
8693
accounts = lsa.GetPrivileges(UserRightsAssignment.SeBatchLogonRight);
@@ -90,13 +97,13 @@ namespace TestApp {
9097
Console.WriteLine(account);
9198
}
9299

93-
Console.WriteLine("[*] Accessing server - Displaying Current");
100+
Console.WriteLine("[*] Removing a principal and listing again");
94101

95102
using (LsaWrapper lsa = new LsaWrapper()) {
96103
lsa.RemovePrivileges("EVOTEC\\przemyslaw.klys", UserRightsAssignment.SeBatchLogonRight);
97104
}
98105

99-
using (LsaWrapper lsa = new LsaWrapper("")) {
106+
using (LsaWrapper lsa = new LsaWrapper()) {
100107
accounts = lsa.GetPrivileges(UserRightsAssignment.SeBatchLogonRight);
101108
}
102109

@@ -108,7 +115,7 @@ namespace TestApp {
108115
}
109116
```
110117

111-
### Example Remote Computer
118+
### Example Remote Computer (LSA wrapper)
112119

113120
```csharp
114121
using System;
@@ -129,7 +136,7 @@ namespace TestApp {
129136
Console.WriteLine(account);
130137
}
131138

132-
Console.WriteLine("[*] Adding Account to the Server");
139+
Console.WriteLine("[*] Granting right to an account");
133140

134141
using (LsaWrapper lsa = new LsaWrapper("AD1")) {
135142
lsa.AddPrivileges("EVOTEC\\przemyslaw.klys", UserRightsAssignment.SeBatchLogonRight);
@@ -145,7 +152,7 @@ namespace TestApp {
145152
Console.WriteLine(account);
146153
}
147154

148-
Console.WriteLine("[*] Accessing AD1 server - Displaying Current");
155+
Console.WriteLine("[*] Removing the principal and listing again");
149156

150157
using (LsaWrapper lsa = new LsaWrapper("AD1")) {
151158
lsa.RemovePrivileges("EVOTEC\\przemyslaw.klys", UserRightsAssignment.SeBatchLogonRight);
@@ -159,6 +166,75 @@ namespace TestApp {
159166
Console.WriteLine(account);
160167
}
161168
}
162-
}
163169
}
164-
```
170+
}
171+
```
172+
173+
### Typed, OO API
174+
175+
```csharp
176+
using LocalSecurityEditor;
177+
178+
// Enumerate all rights (local)
179+
var all = UserRights.Get();
180+
foreach (var ura in all) {
181+
Console.WriteLine($"{ura.ShortName}: {ura.Count} principals");
182+
}
183+
184+
// Lazy streaming
185+
foreach (var ura in new UserRights().EnumerateLazy()) {
186+
Console.WriteLine(ura);
187+
}
188+
189+
// Single right as typed object with principals
190+
var svc = UserRightsAssignment.SeServiceLogonRight.Get();
191+
foreach (var p in svc.Principals) {
192+
Console.WriteLine($"{p.AccountName} -> {p.SidString}");
193+
}
194+
195+
// Remote machine catalog
196+
var allRemote = UserRights.Get("SERVER01");
197+
198+
// Add/Remove/Set via fluent extensions
199+
UserRightsAssignment.SeBatchLogonRight.Add(@"DOMAIN\\svc_batch");
200+
UserRightsAssignment.SeBatchLogonRight.Remove(@"DOMAIN\\old_user");
201+
var result = UserRightsAssignment.SeDenyRemoteInteractiveLogonRight.Set(new[]{ @"DOMAIN\\contractor1", @"DOMAIN\\contractor2"});
202+
Console.WriteLine(result); // e.g., SeDenyRemoteInteractiveLogonRight: +1 -0
203+
204+
// Batching with a manager (remote)
205+
using (var ur = new UserRights("SERVER01")) {
206+
ur.Add(UserRightsAssignment.SeBatchLogonRight, new [] { @"DOMAIN\\svc_batch" });
207+
var summary = ur.Set(UserRightsAssignment.SeServiceLogonRight, new [] { @"DOMAIN\\svc_svc" });
208+
Console.WriteLine(summary);
209+
}
210+
211+
```
212+
213+
### Async APIs
214+
215+
```csharp
216+
// Single right (local)
217+
var svc = await new UserRights().GetStateAsync(UserRightsAssignment.SeServiceLogonRight, ct);
218+
219+
// Enumerate all (remote)
220+
var all = await new UserRights("SERVER01").EnumerateAsync(ct);
221+
222+
// Fluent async extensions
223+
var svc2 = await UserRightsAssignment.SeServiceLogonRight.GetAsync("SERVER01", ct);
224+
```
225+
226+
### Thread Safety
227+
228+
- The library is safe to use from multiple tasks.
229+
- Internally it uses a reader–writer lock:
230+
- Reads may run in parallel; writes are exclusive; dispose is exclusive.
231+
- Prefer reusing a single `UserRights` instance for batching, or create per-task instances for isolation.
232+
233+
### Generate service SIDs
234+
235+
```csharp
236+
string serviceName = "ADSync";
237+
string serviceExpectedSid = "S-1-5-80-3245704983-3664226991-764670653-2504430226-901976451";
238+
string serviceSid = NTService.GenerateSID(serviceName);
239+
Console.WriteLine($"The SID for the service '{serviceName}' is: {serviceSid} {serviceExpectedSid} {(serviceSid == serviceExpectedSid)}");
240+
```

CHANGELOG.MD

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
#### LocalSecurityEditor Release History
22

3+
#### Unreleased
4+
- Target frameworks: add `net9.0-windows`, keep `net8.0-windows`, `netstandard2.0`; restore `net472` for classic .NET. Dropped obsolete TFMs.
5+
- Strict policy handling restored: `LsaOpenPolicy` uses `POLICY_ALL_ACCESS` and enumeration throws `UnauthorizedAccessException` on `ACCESS_DENIED` (empty only for `NO_MORE_ENTRIES`).
6+
- Added async APIs: `GetStateAsync`, `EnumerateAsync`, `GetByRightAsync`, `GetByShortNameAsync`, `AddAsync`, `RemoveAsync`, `SetAsync` and async extension helpers.
7+
- Performance: cache enum values for `Enumerate`/`GetByRight`/`GetByShortName` to avoid repeated allocations.
8+
- Naming: `Win32SecurityIdentifier.securityIdentifier``SecurityIdentifier` (PascalCase).
9+
- Thread-safety: mark dispose flags as `volatile` in `UserRights` and `Win32SecurityIdentifier`.
10+
- Docs: extensive XML documentation for public APIs; README and Assets README updated (targets badge, OO API prominence, async usage, batching tip).
11+
- Tests: add coverage for `PrincipalInfo.ToString()` and `Win32SecurityIdentifier` pinning (Windows-gated).
12+
313
#### 0.4.0
414
- Add `Net 8.0` support
515
- Add `Net 7.0` support
@@ -16,4 +26,4 @@
1626
- Fixes account/principal transformation that would sometimes cause an error.
1727

1828
#### 0.1.0 - 2022.04.14
19-
- First release
29+
- First release

Docs/index.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,11 @@
77
[UserRightsAssignment](./localsecurityeditor.userrightsassignment.md)
88

99
[Win32SecurityIdentifier](./localsecurityeditor.win32securityidentifier.md)
10+
11+
## New (Typed) API
12+
13+
[UserRights](./localsecurityeditor.userrights.md)
14+
15+
[UserRightState](./localsecurityeditor.userrightstate.md)
16+
17+
[PrincipalInfo](./localsecurityeditor.principalinfo.md)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PrincipalInfo
2+
3+
Namespace: LocalSecurityEditor
4+
5+
Principal descriptor carrying both SID and name information.
6+
7+
## Properties
8+
9+
- `SidString` (`string`) — SID in SDDL format (e.g., `S-1-5-32-544`)
10+
- `Domain` (`string`) — may be empty
11+
- `Name` (`string`) — account name
12+
- `AccountName` (`string`) — domain-qualified `Domain\Name` when available
13+
- `Use` (`SidNameUse`) — classification (User, Group, etc.)
14+
15+
## Example
16+
17+
```csharp
18+
var ura = UserRightsAssignment.SeServiceLogonRight.Get();
19+
foreach (var p in ura.Principals)
20+
{
21+
Console.WriteLine($"{p.AccountName} ({p.Use}) -> {p.SidString}");
22+
}
23+
```
24+
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# UserRights
2+
3+
Namespace: LocalSecurityEditor
4+
5+
High-level facade for listing and managing User Rights Assignments (URAs).
6+
7+
## Static Methods
8+
9+
`UserRights.Get()`
10+
11+
- Returns: `IReadOnlyList<UserRightState>` — one object per user right with principals.
12+
13+
`UserRights.Get(string systemName)`
14+
15+
- Returns: `IReadOnlyList<UserRightState>` for a remote machine.
16+
17+
## Instance Methods
18+
19+
`Enumerate()``IReadOnlyList<UserRightState>`
20+
21+
`GetState(UserRightsAssignment right)``UserRightState`
22+
23+
`GetByRight()``Dictionary<UserRightsAssignment, UserRightState>`
24+
25+
`GetByShortName(StringComparer comparer = null)``Dictionary<string, UserRightState>`
26+
27+
`Add(UserRightsAssignment right, IEnumerable<string> principals)`
28+
29+
`Remove(UserRightsAssignment right, IEnumerable<string> principals)`
30+
31+
`Set(UserRightsAssignment right, IEnumerable<string> principals)``UserRightSetResult`
32+
33+
## Examples
34+
35+
```csharp
36+
// All URAs (local)
37+
var all = UserRights.Get();
38+
39+
// Single URA
40+
var svc = new UserRights().GetState(UserRightsAssignment.SeServiceLogonRight);
41+
42+
// Remote
43+
var allRemote = UserRights.Get("SERVER01");
44+
```
45+
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# UserRightState
2+
3+
Namespace: LocalSecurityEditor
4+
5+
Represents a configured User Right with its principals.
6+
7+
## Properties
8+
9+
- `Right` (`UserRightsAssignment`)
10+
- `ShortName` (`string`) — e.g., `SeServiceLogonRight`
11+
- `Name` (`string`) — friendly name
12+
- `Description` (`string`)
13+
- `Principals` (`IReadOnlyList<PrincipalInfo>`)
14+
- `Count` (`int`) — number of principals
15+
16+
## Example
17+
18+
```csharp
19+
var svc = UserRightsAssignment.SeServiceLogonRight.Get();
20+
Console.WriteLine($"{svc.Name} -> {svc.Count}");
21+
foreach (var p in svc.Principals)
22+
{
23+
Console.WriteLine($"{p.AccountName} ({p.SidString})");
24+
}
25+
```
26+

LocalSecurityEditor.Examples/LocalSecurityEditor.Examples.csproj

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<PropertyGroup>
4-
<OutputType>Exe</OutputType>
5-
<TargetFramework>net8.0</TargetFramework>
6-
</PropertyGroup>
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFrameworks>net472;net8.0-windows;net9.0-windows</TargetFrameworks>
6+
</PropertyGroup>
7+
8+
<!-- Ensure restore produces runtime-specific assets for CI invoking win-x86 -->
9+
<PropertyGroup Condition="'$(TargetFramework)' == 'net472'">
10+
<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>
11+
<LangVersion>latest</LangVersion>
12+
</PropertyGroup>
13+
14+
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
15+
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
16+
</ItemGroup>
717

818
<ItemGroup>
919
<ProjectReference Include="..\LocalSecurityEditor\LocalSecurityEditor.csproj" />

0 commit comments

Comments
 (0)