A comprehensive Active Directory management tool built with ASP.NET Core, providing secure and efficient user management capabilities.
- Enhanced parent OU selection UI with improved dropdown design
- Added validation for OU selection during object creation
- Optimized LDAP queries for retrieving organizational units
- Improved error handling when creating objects in selected OUs
- Updated OpenTelemetry integration to version 1.11.2
- Added parent OU selection capability for user, group, and OU creation modals
- Added new dropdown to select target folder or OU when creating objects
- Added GetAllOUs endpoint to retrieve all organizational units for dropdown population
- Added JavaScript utilities for populating and displaying OU selection dropdowns
- Modified user, group, and OU creation forms to include parent OU selection
- Updated creation logic to respect the selected parent OU location
- Improved user interface for object creation workflows
- Added Office 365 integration for user and license management
- Enhanced debug information display with Office 365 debug details
- Added header size information in debug footer
- Improved Office 365 service with detailed logging
- Enhanced session tracking with additional configuration options
- Updated version numbers across the application
- Improved documentation consistency
- Minor code optimizations
- Added StorageManager ViewComponent for managing client-side storage
- Implemented storage visualization UI to view localStorage items and sizes
- Added storage cleanup functionality to clear non-essential storage items
- Added size tracking for localStorage items
- Added protection for essential items to preserve authentication, CSRF, and session data
- Active Directory user management
- Secure authentication and authorization
- Office 365 integration for user and license management
- Rate limiting for sensitive endpoints
- Comprehensive logging system
- Enhanced security headers
- Anti-forgery token protection
- UPN suffix management
- User password management
- Audit logging
- Session tracking and management
- Debug information display
- OpenTelemetry integration with Seq
- Client storage management
- .NET 9.0 SDK or later
- Active Directory domain access
- SQLite (for logging)
- Modern web browser
- Clone the repository:
git clone [repository-url]
cd ADManager- Restore dependencies:
dotnet restore- Build the application:
dotnet build- Run the application:
dotnet run- Copy the sample configuration file:
cp appsettings.sample.json appsettings.json- Edit
appsettings.jsonand replace the demo values with your actual configuration:- Update
ActiveDirectory.DomainandActiveDirectory.LdapPathwith your domain information - Configure
Securitysettings according to your requirements - Set up
Seqserver URL and API key if you're using Seq for logging - Adjust other settings as needed
- Update
The application can be configured through appsettings.json. Key configuration sections include:
"ActiveDirectory": {
"Domain": "your-domain.com",
"LdapPath": "LDAP://DC=your-domain,DC=com"
}"Security": {
"MaxFailedLoginAttempts": 3,
"LockoutTimeInMinutes": 60,
"PasswordPolicy": {
"EnforceComplexity": true,
"MinimumLength": 8,
"RequiredCharacterClasses": 3
},
"AntiForgeryToken": {
"TokenLifetimeInHours": 1,
"RequireHashValidation": true,
"SuppressXFrameOptionsHeader": false
}
}"Debug": {
"EnableDebugFooter": true,
"ShowSessionId": true,
"ShowAntiForgeryInfo": true,
"ShowUserInfo": true,
"ShowHostInfo": true
}"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"ADManager.Middleware": "Debug"
}
},
"Enrich": ["FromLogContext", "WithSessionId", "WithThreadId", "WithMachineName"],
"Properties": {
"Application": "ADManager"
}
}"OpenTelemetry": {
"ServiceName": "ADManager",
"ServiceVersion": "2.2.0",
"EnableConsoleExporter": true,
"ActivitySources": [
"ADManager",
"ADManager.Middleware",
"ADManager.Security"
],
"SessionTracking": {
"Enabled": true,
"SessionIdHeader": "X-Session-ID",
"SessionTimeoutMinutes": 30
}
}"Office365": {
"TenantId": "your-tenant-id",
"ClientId": "your-client-id",
"ClientSecret": "your-client-secret"
}- Form-based authentication
- Windows Authentication support
- Cookie-based session management
- Account lockout protection
- Password complexity requirements
- Configurable request limits per endpoint
- IP-based tracking
- Customizable time windows
- Proxy support
- Content Security Policy
- XSS Protection
- Clickjacking Protection
- MIME-type Sniffing Protection
- Referrer Policy
- Permissions Policy
- Double validation system
- Secure cookie storage
- Audit logging
- JavaScript integration
- Unique session ID generation
- Configurable session timeout
- Cross-request correlation
- Integration with logging and telemetry
- SQLite-based logging (always enabled)
- Location:
{AppRoot}/logs.db - Table:
Logs
- Seq integration
- OpenTelemetry support
- Configurable log levels
- Custom log paths
- Activity source tracking
- Request/response correlation
- Cross-component tracing
- Performance metrics collection
- Exception and error recording
The application includes a configurable debug information footer that can display:
- Current session ID
- Anti-forgery token information
- Authenticated user details
- Host information
- Application version
This can be enabled or disabled via the Debug configuration section in appsettings.json.
ADManager/
├── Controllers/ # API and MVC controllers
├── Models/ # Data models and view models
├── Services/ # Business logic and services
├── Middleware/ # Custom middleware components
├── Filters/ # Action and exception filters
├── TagHelpers/ # Custom tag helpers for views
├── Logging/ # Logging extensions and enrichers
├── Attributes/ # Custom attributes
├── Data/ # Data access layer and context
├── Views/ # Razor views
├── wwwroot/ # Static files
└── Program.cs # Application entry point
dotnet publish -c Release- Fork the repository
- Create a feature branch
- Commit your changes
- Push to the branch
- Create a Pull Request
see LICENSE.md
For support, please create an issue or contact the development team.
The application uses a flexible logging system with these components:
- SQLite Logging: All logs are saved to a SQLite database by default. This logging cannot be disabled and ensures that your application always maintains a log history.
- Default location:
{AppRoot}/logs.db - Table:
Logs
- Default location:
The following logging providers can be enabled or disabled via configuration in appsettings.json:
- Enable/disable by setting
Seq:Enabledtotrueorfalseinappsettings.json - Configure the Seq server URL with
Seq:ServerUrl - Configure the API key with
Seq:ApiKey - Enable session tracking with
Seq:SessionTracking:Enabled
- Enable/disable by setting
OpenTelemetry:Enabledtotrueorfalseinappsettings.json - OpenTelemetry is configured to send data to Seq if Seq is also enabled
- Configure service name with
OpenTelemetry:ServiceName - Configure service version with
OpenTelemetry:ServiceVersion - Configure activity sources with
OpenTelemetry:ActivitySources - Configure session tracking with
OpenTelemetry:SessionTracking
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning",
"ADManager.Middleware": "Debug"
}
},
"Enrich": ["FromLogContext", "WithSessionId", "WithThreadId", "WithMachineName"],
"Properties": {
"Application": "ADManager"
}
},
"OpenTelemetry": {
"ServiceName": "ADManager",
"ServiceVersion": "1.2.0",
"EnableConsoleExporter": true,
"ActivitySources": [
"ADManager",
"ADManager.Middleware",
"ADManager.Security"
],
"SessionTracking": {
"Enabled": true,
"SessionIdHeader": "X-Session-ID",
"SessionTimeoutMinutes": 30
}
},
"Seq": {
"ServerUrl": "https://your-seq-server",
"ApiKey": "your-api-key",
"SessionTracking": {
"Enabled": true,
"SessionIdProperty": "SessionId",
"SessionTimeoutMinutes": 30
}
}
}- For SQLite logging, you can use a relative path like
logs.dbwhich will save the logs in the application's root directory, or an absolute path likeC:\Logs\admanager-logs.db - The application will automatically handle the path resolution, even if you specify a relative path
The application includes a rate limiting middleware to protect sensitive endpoints from brute force attacks and other abusive behavior.
-
Protected Endpoints: The rate limiter targets authentication and password-related endpoints:
/auth/login/resetpassword/changepassword
-
Default Limits: By default, the rate limiter allows 15 requests per minute for each client IP address per endpoint.
Rate limiting is configured in the Program.cs file:
app.UseRateLimiting(maxRequests: 15, timeWindowSeconds: 60);Parameters:
maxRequests: Maximum number of requests allowed within the time window (default: 15)timeWindowSeconds: The time window in seconds for counting requests (default: 60)
- Each client is identified by their IP address
- The middleware tracks requests for each client IP + endpoint combination
- When a client exceeds the request limit for a specific endpoint, they receive a 429 (Too Many Requests) response
- The response includes a "Retry-After" header indicating when they can retry
To adjust rate limiting settings, modify the middleware configuration in Program.cs. Examples:
// More permissive: 30 requests per minute
app.UseRateLimiting(maxRequests: 30, timeWindowSeconds: 60);
// More restrictive: 5 requests per 2 minutes
app.UseRateLimiting(maxRequests: 5, timeWindowSeconds: 120);If your application is behind a proxy or load balancer, the middleware will use the X-Forwarded-For header to determine the client's IP address. Ensure your proxy is configured to set this header correctly.
The application implements security headers middleware to enhance web security by setting various HTTP headers that help protect against common web vulnerabilities.
The middleware automatically adds the following security headers to all responses:
| Header | Value | Purpose |
|---|---|---|
| Content-Security-Policy-Report-Only | Complex policy | Controls resources the browser is allowed to load (report-only mode) |
| X-Content-Type-Options | nosniff | Prevents MIME-type sniffing |
| X-Frame-Options | SAMEORIGIN | Prevents clickjacking by forbidding embedding in frames from other origins |
| X-XSS-Protection | 1; mode=block | Enables browser's XSS filtering |
| Referrer-Policy | strict-origin-when-cross-origin | Controls information in the Referer header |
| Permissions-Policy | Various restrictions | Restricts access to browser features |
The application uses a Content Security Policy in report-only mode, which means violations are logged but not blocked. The policy includes:
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://code.jquery.com https://*.microsoft.com;
style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com;
img-src 'self' data: https:;
font-src 'self' data: https://fonts.gstatic.com;
connect-src 'self' https:;
frame-ancestors 'self';
form-action 'self';
base-uri 'self';
object-src 'none'
Security headers are enabled in Program.cs with:
app.UseSecurityHeaders(enableEnforcedCsp: false);The enableEnforcedCsp parameter allows switching between enforcement mode and report-only mode for the Content Security Policy.
The application includes a session tracking middleware that generates and maintains unique session IDs across requests, enabling better tracking and correlation of user activity.
- Unique Session ID Generation: Creates a cryptographically secure unique ID for each session
- Cross-Request Correlation: Maintains the same session ID across multiple requests from the same client
- Integration with Logging: Enriches logs with session IDs for easier debugging and audit trails
- Integration with Telemetry: Adds session context to OpenTelemetry spans
- Configurable Timeout: Session expiration can be configured in minutes
Session tracking is configured in the appsettings.json file:
"OpenTelemetry": {
"SessionTracking": {
"Enabled": true,
"SessionIdHeader": "X-Session-ID",
"SessionTimeoutMinutes": 30
}
}Parameters:
Enabled: Whether session tracking is enabled (default: true)SessionIdHeader: The HTTP header used to transmit the session ID (default: "X-Session-ID")SessionTimeoutMinutes: The session timeout in minutes (default: 30)
Session tracking is implemented in SessionTrackingMiddleware.cs and is enabled in Program.cs:
app.UseMiddleware<SessionTrackingMiddleware>();- Session IDs are generated using a cryptographically secure random number generator
- The middleware uses cookies with secure flags to store session information
- Session IDs are not directly tied to authentication tokens, providing an additional layer of tracking
The application implements enhanced anti-forgery token validation to protect against Cross-Site Request Forgery (CSRF) attacks. This protection goes beyond the default ASP.NET Core implementation with additional security layers.
- Double Validation: Validates tokens using both ASP.NET Core's built-in validation and a custom SHA-256 hash validation
- Global Protection: Automatically protects all POST, PUT, DELETE, and PATCH requests
- Secure Cookie Storage: Stores token hash in a secure, HTTP-only cookie with SameSite=Strict
- Audit Logging: Records validation failures with user information and IP address
- JavaScript Integration: Ensures all AJAX requests include anti-forgery tokens
Anti-forgery token settings are configured in the Security:AntiForgeryToken section of appsettings.json:
"Security": {
"AntiForgeryToken": {
"TokenLifetimeInHours": 1,
"RequireHashValidation": true,
"SuppressXFrameOptionsHeader": false
}
}| Setting | Description |
|---|---|
| TokenLifetimeInHours | How long tokens remain valid (default: 1 hour) |
| RequireHashValidation | Whether to perform the additional hash validation (default: true) |
| SuppressXFrameOptionsHeader | Whether to suppress the X-Frame-Options header (default: false) |
The enhanced anti-forgery validation is implemented using a custom middleware and is enabled in Program.cs:
// Add global anti-forgery validation
builder.Services.AddAntiforgery(options => {
options.Cookie.Name = "X-CSRF-TOKEN";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
options.HeaderName = "RequestVerificationToken";
options.FormFieldName = "__RequestVerificationToken";
});
// In the pipeline configuration:
app.UseEnhancedAntiForgery();The application includes JavaScript that automatically adds anti-forgery tokens to all AJAX requests:
- For JSON requests, the token is included in the "RequestVerificationToken" header
- For form submissions, the token is included as a hidden form field
- For FormData objects, the token is appended to the data
- For URL-encoded data, the token is included as a parameter
- Protection Against CSRF: Prevents attackers from making unauthorized requests on behalf of authenticated users
- Token Hash Validation: Makes it significantly harder to forge valid tokens, even if other security measures are compromised
- Limited Token Lifetime: Reduces the window of opportunity for attacks if a token is somehow leaked
- Transparent UX: Automatically handles token acquisition and validation without disrupting the user experience
The application includes a client storage management component that helps manage browser-based storage to keep cookies within RFC standards (under 4096 bytes) by using localStorage for non-sensitive data.
- Storage Visualization: Provides a UI to view all localStorage items and their sizes
- Storage Cleanup: Allows users to clear non-essential storage items
- Essential Item Protection: Preserves authentication, CSRF, and session-related items
- Size Tracking: Displays the total size of all localStorage items
The storage manager is implemented as a ViewComponent and can be included in any page with:
@await Component.InvokeAsync("StorageManager")- The storage manager preserves authentication, CSRF token, and session-related data
- Only client-side storage items are affected; server-side data remains intact
- The component is designed for administrative use and should be included only in admin pages
The application uses SQLite as its database engine. The database connection is configured in the ConnectionStrings section of appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Data Source=|DataDirectory|\\admanager.db"
}The application supports the following formats for the connection string:
-
Using DataDirectory substitution:
"Data Source=|DataDirectory|\\admanager.db"The
|DataDirectory|token is replaced with the application's base directory at runtime. -
Using relative path:
"Data Source=admanager.db"The database file will be created in the application's working directory.
-
Using absolute path:
"Data Source=C:\\Path\\To\\admanager.db"The database file will be created at the specified location.
The application automatically creates the database if it doesn't exist. This process is handled during application startup in Program.cs. When the application starts, it:
- Checks if the database exists
- Creates the database if it doesn't exist
- Creates all necessary tables based on the application's data models
- Logs information about the database creation and tables
No additional setup or migration commands are required to initialize the database.
The AD Manager now includes Office 365 integration capabilities, allowing you to manage users across both AD and Office 365 environments. This integration enables:
- Viewing all Office 365 users
- Blocking/unblocking user sign-ins
- Managing user licenses
- Configuring service plans
- Synchronizing users between AD and Office 365
To enable Office 365 integration, you need to register an application in the Azure portal and grant it the necessary permissions to interact with Microsoft Graph API. Follow these steps:
- Sign in to the Azure Portal
- Navigate to Azure Active Directory > App registrations
- Click New registration
- Enter a name for your application (e.g., "AD Manager")
- For the Supported account types, select "Accounts in this organizational directory only"
- Leave the Redirect URI blank (not needed for this application)
- Click Register
After registering your application:
- The Tenant ID is displayed in the Overview page as "Directory (tenant) ID"
- The Client ID is displayed as "Application (client) ID"
- Copy both values and save them for later
- In your application's page, navigate to Certificates & secrets
- Click New client secret
- Add a description (e.g., "AD Manager Secret") and select an expiration period
- Click Add
- IMPORTANT: Copy the generated client secret value immediately and save it. This value is only shown once and cannot be retrieved later!
Your application needs appropriate permissions to manage Office 365 users and licenses:
- In your application's page, navigate to API permissions
- Click Add a permission
- Select Microsoft Graph
- Select Application permissions (not delegated permissions)
- Add the following permissions:
User.Read.All(Read all users' full profiles)User.ReadWrite.All(Read and write all users' full profiles)Directory.Read.All(Read directory data)Directory.ReadWrite.All(Read and write directory data)Organization.Read.All(Read organization information)UserAuthenticationMethod.ReadWrite.All(For blocking/unblocking sign-in)LicenseAssignment.ReadWrite.All(Manage license assignments)
- Click Add permissions
- Click Grant admin consent for [Your Organization] button at the top of the permissions page
- Update your
appsettings.jsonfile with the values obtained above:
"Office365": {
"TenantId": "your-tenant-id-from-step-2",
"ClientId": "your-client-id-from-step-2",
"ClientSecret": "your-client-secret-from-step-3"
}- Restart the application to apply the changes
- The client secret allows administrative access to your Office 365 tenant. Keep it secure.
- Consider using Azure Key Vault or another secrets management solution for production environments.
- Regularly rotate the client secret (create a new one and update your configuration).
- Use the minimum set of permissions required for your specific use case.
- Implement proper audit logging for all Office 365 management actions.
If you encounter issues with the Office 365 integration:
- Verify that all required permissions have been granted and admin consent provided.
- Ensure your client secret hasn't expired.
- Check network connectivity to the Microsoft Graph API endpoints.
- Review application logs for detailed error messages.
- Verify that the account used to grant consent has appropriate Office 365 administrator permissions.