Reorganize infrastructure as code and implement versioning schema

This commit is contained in:
Alphaeus Mote
2025-05-21 19:40:57 -04:00
parent 8d1a6dd188
commit 2db108aa6b
34 changed files with 4870 additions and 870 deletions
-69
View File
@@ -1,69 +0,0 @@
name: Docker
on:
push:
branches: [ "main" ]
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
env:
# Use docker.io for Docker Hub if empty
REGISTRY: docker.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is needed for token authentication
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf
# Login against a Docker registry
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
# Build and push Docker image with Buildx
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
+7 -3
View File
@@ -53,7 +53,7 @@ This document provides detailed instructions for deploying the Active Directory
```bash
cp .env.example .env
```
Edit the .env file with your specific configuration:
```bash
nano .env
@@ -62,9 +62,9 @@ This document provides detailed instructions for deploying the Active Directory
3. **SSL Configuration**
For production environments, the application is designed to work behind a reverse proxy or load balancer that handles SSL termination.
If you need to implement SSL directly in your environment, consider:
- Using a reverse proxy like Nginx or Traefik in front of the application
- Setting up SSL termination at the infrastructure level (e.g., using AWS Application Load Balancer)
- Configuring your cloud provider's SSL management services
@@ -391,6 +391,8 @@ For deployment on Microsoft Azure:
Detailed Azure deployment instructions can be found in [AZURE_DEPLOYMENT.md](AZURE_DEPLOYMENT.md).
For comprehensive Kubernetes deployment instructions for any cloud provider, refer to the [Kubernetes Deployment Guide](docs/kubernetes-deployment.md).
### Google Cloud Deployment
For deployment on Google Cloud Platform:
@@ -411,6 +413,8 @@ For deployment on Google Cloud Platform:
Detailed GCP deployment instructions can be found in [GCP_DEPLOYMENT.md](GCP_DEPLOYMENT.md).
For comprehensive Kubernetes deployment instructions for any cloud provider, refer to the [Kubernetes Deployment Guide](docs/kubernetes-deployment.md).
## Production Best Practices
### Security Considerations
+18 -4
View File
@@ -23,6 +23,9 @@ The Active Directory Management Platform provides the following key features:
### Core Functionality
- **User Management**: Create, read, update, and delete Active Directory users with comprehensive attribute support
- Password reset with policy validation options
- Account enabling/disabling
- Bulk account management operations
- **Group Management**: Manage security and distribution groups, including nested group structures
- **Organizational Unit (OU) Management**: Create and manage the hierarchical OU structure
- **Computer Management**: Manage computer accounts with detailed property configuration
@@ -76,6 +79,8 @@ The Active Directory Management Platform provides the following key features:
### DevOps
- **Docker**: Containerization platform for consistent deployment
- **Kubernetes**: Container orchestration for scalable deployments
- **Infrastructure as Code (IaC)**: Deployment configurations for Docker and Kubernetes
## Architecture
@@ -111,8 +116,8 @@ The application follows a modern web architecture with clear separation of conce
3. Create a .env file from the example:
```bash
cp .env.example .env
# Edit .env with your configuration
cp iac/docker/.env.example iac/docker/.env
# Edit iac/docker/.env with your configuration
```
4. Set up the database:
@@ -142,8 +147,8 @@ The application follows a modern web architecture with clear separation of conce
2. Create a .env file from the example:
```bash
cp .env.example .env
# Edit .env with your production configuration
cp iac/docker/.env.example iac/docker/.env
# Edit iac/docker/.env with your production configuration
```
3. Set up environment variables for production:
@@ -155,6 +160,7 @@ The application follows a modern web architecture with clear separation of conce
4. Start the application stack:
```bash
cd iac/docker
docker-compose up -d
```
@@ -187,6 +193,10 @@ The application is also available as a pre-built Docker image:
For manual deployment instructions, refer to the [Deployment Guide](DEPLOYMENT.md).
#### Kubernetes Deployment
For Kubernetes deployment instructions, refer to the [Kubernetes Deployment Guide](docs/kubernetes-deployment.md).
## API Documentation
The API is thoroughly documented using the OpenAPI specification (Swagger):
@@ -202,6 +212,10 @@ The API is thoroughly documented using the OpenAPI specification (Swagger):
- **Response Format**: Consistent JSON structure with appropriate status codes
- **Error Handling**: Descriptive error messages and appropriate HTTP status codes
### API Examples
For examples of using the API, including the password reset and account management endpoints, refer to the [API Examples Guide](docs/api-examples.md).
### Filterable Endpoints
All list endpoints support filtering with a flexible query syntax:
+319
View File
@@ -0,0 +1,319 @@
# API Examples
This document provides examples of using the Active Directory Management API, including the new password reset and account management endpoints.
## Table of Contents
- [Authentication](#authentication)
- [User Management](#user-management)
- [Password Management](#password-management)
- [Account Status Management](#account-status-management)
- [Group Management](#group-management)
- [Bulk Operations](#bulk-operations)
- [Error Handling](#error-handling)
## Authentication
### Login
```bash
curl -X POST http://localhost:5000/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"username": "admin",
"password": "your-password"
}'
```
Response:
```json
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "admin",
"email": "admin@example.com",
"fullName": "System Administrator",
"role": "admin"
}
}
```
### Using the Token
For all subsequent requests, include the token in the Authorization header:
```bash
curl -X GET http://localhost:5000/api/users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
## User Management
### Get All Users
```bash
curl -X GET http://localhost:5000/api/connections/1/ad-users \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
### Get User by ObjectGUID
```bash
curl -X GET http://localhost:5000/api/connections/1/ad-users/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
### Create User
```bash
curl -X POST http://localhost:5000/api/connections/1/ad-users \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"sAMAccountName": "jdoe",
"givenName": "John",
"sn": "Doe",
"displayName": "John Doe",
"mail": "jdoe@example.com",
"userPrincipalName": "jdoe@example.com",
"parentOU": "OU=Users,DC=example,DC=com",
"password": "SecurePassword123!",
"enabled": true
}'
```
### Update User
```bash
curl -X PATCH http://localhost:5000/api/connections/1/ad-users/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"displayName": "John A. Doe",
"mail": "john.doe@example.com",
"telephoneNumber": "+1 (555) 123-4567"
}'
```
### Delete User
```bash
curl -X DELETE http://localhost:5000/api/connections/1/ad-users/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
## Password Management
### Reset User Password
```bash
curl -X POST http://localhost:5000/api/connections/1/reset-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userObjectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"newPassword": "NewSecurePassword456!",
"skipValidation": false,
"requirePasswordChangeAtNextLogon": true
}'
```
Response:
```json
{
"success": true,
"message": "Password reset successful for user with GUID a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```
### Reset Password with Skip Validation
For scenarios where you need to bypass password policy validation:
```bash
curl -X POST http://localhost:5000/api/connections/1/reset-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userObjectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"newPassword": "TempPass123",
"skipValidation": true,
"requirePasswordChangeAtNextLogon": true
}'
```
### Reset Password Without Requiring Change at Next Logon
```bash
curl -X POST http://localhost:5000/api/connections/1/reset-password \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userObjectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"newPassword": "NewSecurePassword456!",
"requirePasswordChangeAtNextLogon": false
}'
```
## Account Status Management
### Enable User Account
```bash
curl -X POST http://localhost:5000/api/connections/1/enable-user-account \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userObjectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"enabled": true
}'
```
Response:
```json
{
"success": true,
"message": "User account enabled successfully for user with GUID a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```
### Disable User Account
```bash
curl -X POST http://localhost:5000/api/connections/1/enable-user-account \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userObjectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"enabled": false
}'
```
Response:
```json
{
"success": true,
"message": "User account disabled successfully for user with GUID a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
```
### Bulk Enable User Accounts
```bash
curl -X POST http://localhost:5000/api/connections/1/bulk-enable-user-accounts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userDNs": [
"CN=John Doe,OU=Users,DC=example,DC=com",
"CN=Jane Smith,OU=Users,DC=example,DC=com",
"CN=Bob Johnson,OU=Users,DC=example,DC=com"
],
"enabled": true
}'
```
Response:
```json
{
"success": [
"CN=John Doe,OU=Users,DC=example,DC=com",
"CN=Jane Smith,OU=Users,DC=example,DC=com",
"CN=Bob Johnson,OU=Users,DC=example,DC=com"
],
"failed": []
}
```
### Bulk Disable User Accounts
```bash
curl -X POST http://localhost:5000/api/connections/1/bulk-enable-user-accounts \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"userDNs": [
"CN=John Doe,OU=Users,DC=example,DC=com",
"CN=Jane Smith,OU=Users,DC=example,DC=com",
"CN=Bob Johnson,OU=Users,DC=example,DC=com"
],
"enabled": false
}'
```
## Group Management
### Add User to Group
```bash
curl -X POST http://localhost:5000/api/connections/1/add-to-group \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"objectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"groupObjectGUID": "g1h2i3j4-k5l6-7890-mnop-qr1234567890",
"objectType": "user"
}'
```
### Remove User from Group
```bash
curl -X POST http://localhost:5000/api/connections/1/remove-from-group \
-H "Content-Type: application/json" \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
-d '{
"objectGUID": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"groupObjectGUID": "g1h2i3j4-k5l6-7890-mnop-qr1234567890",
"objectType": "user"
}'
```
## Error Handling
### Invalid Request
```json
{
"errors": [
{
"code": "invalid_type",
"expected": "string",
"received": "undefined",
"path": ["userObjectGUID"],
"message": "User ObjectGUID is required"
}
]
}
```
### User Not Found
```json
{
"message": "User not found"
}
```
### Permission Denied
```json
{
"message": "You do not have permission to perform this action"
}
```
### LDAP Operation Failed
```json
{
"message": "Failed to reset password",
"error": "LDAP operation failed: Constraint violation"
}
```
+316
View File
@@ -0,0 +1,316 @@
# Deployment Guide
This guide provides instructions for deploying the Active Directory Management application using Docker, Docker Compose, and Kubernetes.
## Table of Contents
- [Docker Deployment](#docker-deployment)
- [Building the Docker Image](#building-the-docker-image)
- [Running with Docker](#running-with-docker)
- [Environment Variables](#environment-variables)
- [Docker Compose Deployment](#docker-compose-deployment)
- [Configuration](#configuration)
- [Starting the Services](#starting-the-services)
- [Stopping the Services](#stopping-the-services)
- [Kubernetes Deployment](#kubernetes-deployment)
- [Prerequisites](#prerequisites)
- [Configuration](#kubernetes-configuration)
- [Deployment](#deployment)
- [Accessing the Application](#accessing-the-application)
- [Scaling](#scaling)
- [Updating](#updating)
- [Monitoring](#monitoring)
- [Production Considerations](#production-considerations)
- [Security](#security)
- [Backups](#backups)
- [High Availability](#high-availability)
## Docker Deployment
### Building the Docker Image
To build the Docker image using the provided build scripts:
#### Linux/macOS:
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Make the script executable
chmod +x build.sh
# Build the image with default settings
./build.sh
# Build with custom tag
./build.sh --tag v1.0.0
# Build and push to a registry
./build.sh --registry your-registry.com --tag v1.0.0 --push
```
#### Windows:
```powershell
# Navigate to the iac/docker directory
cd iac\docker
# Build the image with default settings
.\build.ps1
# Build with custom tag
.\build.ps1 -tag v1.0.0
# Build and push to a registry
.\build.ps1 -registry your-registry.com -tag v1.0.0 -push
```
#### Manually:
```bash
# Navigate to the project root
cd /path/to/project
# Build the image
docker build -t ad-management:latest -f iac/docker/Dockerfile .
```
### Running with Docker
To run the application with Docker:
```bash
docker run -d --name ad-management \
-p 5000:5000 \
-e NODE_ENV=production \
-e POSTGRES_HOST=your-postgres-host \
-e POSTGRES_PORT=5432 \
-e POSTGRES_DB=admgr \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=your-password \
-e REDIS_HOST=your-redis-host \
-e REDIS_PORT=6379 \
-e JWT_SECRET=your-jwt-secret \
-e SESSION_SECRET=your-session-secret \
ad-management:latest
```
### Environment Variables
The application uses the following environment variables:
| Variable | Description | Default |
|----------|-------------|---------|
| `NODE_ENV` | Environment mode | `development` |
| `PORT` | Port to run the server on | `5000` |
| `BASE_URL` | Base URL for the application | `http://localhost:5000` |
| `POSTGRES_HOST` | PostgreSQL host | `localhost` |
| `POSTGRES_PORT` | PostgreSQL port | `5432` |
| `POSTGRES_DB` | PostgreSQL database name | `admgr` |
| `POSTGRES_USER` | PostgreSQL username | `postgres` |
| `POSTGRES_PASSWORD` | PostgreSQL password | - |
| `REDIS_HOST` | Redis host | `localhost` |
| `REDIS_PORT` | Redis port | `6379` |
| `JWT_SECRET` | Secret for JWT tokens | - |
| `SESSION_SECRET` | Secret for session cookies | - |
| `DEFAULT_ADMIN_USERNAME` | Default admin username | `admin` |
| `DEFAULT_ADMIN_PASSWORD` | Default admin password | - |
| `DEFAULT_ADMIN_EMAIL` | Default admin email | - |
| `DEFAULT_ADMIN_FULLNAME` | Default admin full name | `System Administrator` |
| `DISABLE_REGISTRATION` | Disable user registration | `false` |
## Docker Compose Deployment
### Configuration
The application includes a `docker-compose.yml` file for easy deployment. You can customize the environment variables by creating or modifying the `.env` file in the `iac/docker` directory.
Example `.env` file:
```
NODE_ENV=production
PORT=5000
BASE_URL=http://localhost:5000
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
POSTGRES_DB=admgr
POSTGRES_USER=postgres
POSTGRES_PASSWORD=your-postgres-password
REDIS_HOST=redis
REDIS_PORT=6379
JWT_SECRET=your-jwt-secret
SESSION_SECRET=your-session-secret
DEFAULT_ADMIN_USERNAME=admin
DEFAULT_ADMIN_PASSWORD=your-admin-password
DEFAULT_ADMIN_EMAIL=admin@example.com
DEFAULT_ADMIN_FULLNAME=System Administrator
DISABLE_REGISTRATION=false
```
### Starting the Services
To start all services:
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Start the application stack
docker-compose up -d
```
This will start the application, PostgreSQL, and Redis containers.
### Stopping the Services
To stop all services:
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Stop the application stack
docker-compose down
```
To stop and remove all data volumes:
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Stop the application stack and remove volumes
docker-compose down -v
```
## Kubernetes Deployment
### Prerequisites
- Kubernetes cluster (v1.19+)
- kubectl configured to communicate with your cluster
- [Kustomize](https://kustomize.io/) (v4.0+) or kubectl v1.14+ which includes kustomize
- Optional: Ingress controller (e.g., NGINX Ingress Controller)
- Optional: cert-manager for TLS certificates
### Kubernetes Configuration
The Kubernetes manifests are located in the `iac/kubernetes/` directory. Before deploying, you should customize the following files:
1. `iac/kubernetes/configmap.yaml`: Update environment variables
2. `iac/kubernetes/secrets.yaml`: Update secrets (use base64-encoded values)
3. `iac/kubernetes/deployment.yaml`: Update resource limits if needed
4. `iac/kubernetes/ingress.yaml`: Update host name and TLS configuration
### Deployment
To deploy the application to Kubernetes:
```bash
# Navigate to the iac/kubernetes directory
cd iac/kubernetes
# Apply all resources
kubectl apply -k .
# Or from anywhere in the project
kubectl apply -k iac/kubernetes
```
This will create:
- A namespace called `ad-management`
- ConfigMap and Secret for environment variables
- PostgreSQL StatefulSet with persistent storage
- Redis StatefulSet with persistent storage
- Application Deployment with 2 replicas
- Services for all components
- Ingress for external access
### Accessing the Application
If you've configured the Ingress, the application will be available at the hostname specified in `kubernetes/ingress.yaml`.
Without Ingress, you can use port-forwarding to access the application:
```bash
kubectl port-forward -n ad-management svc/ad-management 5000:80
```
Then access the application at http://localhost:5000.
### Scaling
To scale the application:
```bash
kubectl scale -n ad-management deployment/ad-management --replicas=3
```
### Updating
To update the application to a new version:
1. Build and push the new Docker image
2. Update the image tag in `kubernetes/deployment.yaml`
3. Apply the changes:
```bash
kubectl apply -k kubernetes/
```
Or use kubectl set image:
```bash
kubectl set image -n ad-management deployment/ad-management ad-management=ad-management:new-tag
```
### Monitoring
You can monitor the application using:
```bash
# Check pod status
kubectl get pods -n ad-management
# View logs
kubectl logs -n ad-management deployment/ad-management
# Describe deployment
kubectl describe deployment -n ad-management ad-management
```
## Production Considerations
### Security
For production deployments:
1. Use strong, unique passwords for all secrets
2. Enable TLS for all connections
3. Use network policies to restrict traffic between components
4. Set up proper RBAC for Kubernetes resources
5. Regularly update all components and dependencies
6. Consider using a secrets management solution like HashiCorp Vault
### Backups
Set up regular backups for:
1. PostgreSQL database
2. Redis data (if persistence is enabled)
3. Application configuration
Example PostgreSQL backup:
```bash
kubectl exec -n ad-management postgres-0 -- pg_dump -U postgres admgr > backup.sql
```
### High Availability
For high availability:
1. Run multiple replicas of the application
2. Use a PostgreSQL cluster with replication
3. Configure Redis with replication or cluster mode
4. Deploy across multiple availability zones
5. Use a load balancer or ingress controller with multiple backends
6. Implement proper health checks and readiness probes
+324
View File
@@ -0,0 +1,324 @@
# Kubernetes Deployment Guide
This guide provides detailed instructions for deploying the Active Directory Management application on Kubernetes.
## Prerequisites
- Kubernetes cluster (v1.19+)
- kubectl configured to communicate with your cluster
- [Kustomize](https://kustomize.io/) (v4.0+) or kubectl v1.14+ which includes kustomize
- Optional: Ingress controller (e.g., NGINX Ingress Controller)
- Optional: cert-manager for TLS certificates
## Deployment Architecture
The Kubernetes deployment consists of:
- **Application Deployment**: The main application with multiple replicas
- **PostgreSQL StatefulSet**: Database with persistent storage
- **Redis StatefulSet**: Cache and session storage with persistent storage
- **Services**: For internal communication between components
- **Ingress**: For external access to the application
- **ConfigMap and Secrets**: For configuration and sensitive data
All Kubernetes manifests are located in the `iac/kubernetes/` directory.
## Step-by-Step Deployment
### 1. Customize Configuration
Before deploying, customize the configuration files in the `iac/kubernetes/` directory:
#### ConfigMap (`iac/kubernetes/configmap.yaml`)
Update environment variables according to your environment:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: ad-management-config
namespace: ad-management
data:
NODE_ENV: "production"
PORT: "5000"
BASE_URL: "https://your-domain.com" # Update this
DEFAULT_ADMIN_USERNAME: "admin"
DEFAULT_ADMIN_FULLNAME: "System Administrator"
DISABLE_REGISTRATION: "false"
POSTGRES_DB: "admgr"
POSTGRES_USER: "postgres"
```
#### Secrets (`kubernetes/secrets.yaml`)
Update the secrets with your own base64-encoded values:
```bash
# Generate base64-encoded secrets
echo -n "your-jwt-secret" | base64
echo -n "your-session-secret" | base64
echo -n "your-admin-password" | base64
echo -n "admin@your-domain.com" | base64
echo -n "your-postgres-password" | base64
```
Then update `iac/kubernetes/secrets.yaml`:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: ad-management-secrets
namespace: ad-management
type: Opaque
data:
JWT_SECRET: "base64-encoded-jwt-secret"
SESSION_SECRET: "base64-encoded-session-secret"
DEFAULT_ADMIN_PASSWORD: "base64-encoded-admin-password"
DEFAULT_ADMIN_EMAIL: "base64-encoded-admin-email"
POSTGRES_PASSWORD: "base64-encoded-postgres-password"
```
#### Ingress (`iac/kubernetes/ingress.yaml`)
Update the hostname and TLS configuration:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ad-management-ingress
namespace: ad-management
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- your-domain.com # Update this
secretName: ad-management-tls
rules:
- host: your-domain.com # Update this
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ad-management
port:
name: http
```
### 2. Deploy the Application
Deploy all resources using Kustomize:
```bash
# Navigate to the iac/kubernetes directory
cd iac/kubernetes
# Apply all resources
kubectl apply -k .
# Or from anywhere in the project
kubectl apply -k iac/kubernetes
```
### 3. Verify Deployment
Check that all resources are created and running:
```bash
# Check namespace
kubectl get namespace ad-management
# Check pods
kubectl get pods -n ad-management
# Check services
kubectl get services -n ad-management
# Check deployments and statefulsets
kubectl get deployment,statefulset -n ad-management
# Check ingress
kubectl get ingress -n ad-management
```
### 4. Access the Application
If you've configured the Ingress, the application will be available at your specified domain.
Without Ingress, use port-forwarding:
```bash
kubectl port-forward -n ad-management svc/ad-management 5000:80
```
Then access the application at http://localhost:5000.
## Common Operations
### Scaling the Application
To scale the application horizontally:
```bash
kubectl scale -n ad-management deployment/ad-management --replicas=3
```
### Updating the Application
To update the application to a new version:
```bash
# Update the image
kubectl set image -n ad-management deployment/ad-management ad-management=ad-management:new-tag
# Check rollout status
kubectl rollout status -n ad-management deployment/ad-management
```
### Viewing Logs
To view application logs:
```bash
# View logs from all pods
kubectl logs -n ad-management -l app=ad-management
# View logs from a specific pod
kubectl logs -n ad-management pod/ad-management-xxxx-yyyy
```
### Restarting Components
To restart the application:
```bash
kubectl rollout restart -n ad-management deployment/ad-management
```
To restart PostgreSQL or Redis:
```bash
kubectl rollout restart -n ad-management statefulset/postgres
kubectl rollout restart -n ad-management statefulset/redis
```
## Troubleshooting
### Pod Startup Issues
If pods are not starting:
```bash
# Check pod status
kubectl get pods -n ad-management
# Describe the pod for more details
kubectl describe pod -n ad-management pod/ad-management-xxxx-yyyy
# Check container logs
kubectl logs -n ad-management pod/ad-management-xxxx-yyyy
```
### Database Connection Issues
If the application can't connect to the database:
1. Check if the PostgreSQL pod is running:
```bash
kubectl get pods -n ad-management -l app=postgres
```
2. Check PostgreSQL logs:
```bash
kubectl logs -n ad-management pod/postgres-0
```
3. Verify the connection from the application pod:
```bash
kubectl exec -it -n ad-management pod/ad-management-xxxx-yyyy -- nc -zv postgres 5432
```
### Ingress Issues
If the Ingress is not working:
1. Check the Ingress status:
```bash
kubectl describe ingress -n ad-management ad-management-ingress
```
2. Check the Ingress controller logs:
```bash
kubectl logs -n ingress-nginx deployment/ingress-nginx-controller
```
## Examples
### Example: Setting Up TLS with cert-manager
1. Install cert-manager:
```bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.9.1/cert-manager.yaml
```
2. Create a ClusterIssuer for Let's Encrypt:
```yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
```
3. Apply the ClusterIssuer:
```bash
kubectl apply -f cluster-issuer.yaml
```
### Example: Setting Up Monitoring with Prometheus and Grafana
1. Install Prometheus Operator:
```bash
kubectl apply -f https://github.com/prometheus-operator/kube-prometheus/releases/download/v0.10.0/manifests-0.10.0.tar.gz
```
2. Create a ServiceMonitor for the application:
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: ad-management
namespace: monitoring
spec:
selector:
matchLabels:
app: ad-management
namespaceSelector:
matchNames:
- ad-management
endpoints:
- port: http
path: /metrics
interval: 15s
```
3. Apply the ServiceMonitor:
```bash
kubectl apply -f service-monitor.yaml
```
+257
View File
@@ -0,0 +1,257 @@
# Infrastructure as Code (IaC)
This directory contains all the infrastructure as code configurations for deploying the Active Directory Management application.
## Directory Structure
- **docker/**: Docker-related files
- `Dockerfile`: Multi-stage Docker build file
- `docker-compose.yml`: Docker Compose configuration
- `build.sh`: Bash script for building and pushing Docker images (Linux/macOS)
- `build.ps1`: PowerShell script for building and pushing Docker images (Windows)
- `.env`: Environment variables for Docker deployment
- `.env.example`: Example environment variables
- `.env.production`: Production environment variables
- **version.txt**: Central version file in yyyy.MM.dd.HHmm format
- **update-version.sh**: Bash script to update version.txt (Linux/macOS)
- **update-version.ps1**: PowerShell script to update version.txt (Windows)
- **update-all.sh**: Comprehensive script to update all version references (Linux/macOS)
- **update-all.ps1**: Comprehensive script to update all version references (Windows)
- **kubernetes/**: Kubernetes manifests
- `namespace.yaml`: Namespace definition
- `configmap.yaml`: ConfigMap for non-sensitive configuration
- `secrets.yaml`: Secrets for sensitive configuration
- `postgres.yaml`: PostgreSQL StatefulSet and Service
- `redis.yaml`: Redis StatefulSet and Service
- `deployment.yaml`: Application Deployment and Service
- `ingress.yaml`: Ingress for external access
- `kustomization.yaml`: Kustomize configuration
## Docker
### Versioning
The application uses a versioning scheme in the format `yyyy.MM.dd.HHmm` (year, month, day, hour, minute).
#### Comprehensive Version Update
To update the version and apply it to all components (recommended):
On Linux/macOS:
```bash
# Navigate to the iac directory
cd iac
# Make the script executable
chmod +x update-all.sh
# Update all version references
./update-all.sh
```
On Windows:
```powershell
# Navigate to the iac directory
cd iac
# Update all version references
.\update-all.ps1
```
#### Manual Version Update
To update only the version.txt file:
On Linux/macOS:
```bash
# Navigate to the iac directory
cd iac
# Make the script executable
chmod +x update-version.sh
# Update the version
./update-version.sh
```
On Windows:
```powershell
# Navigate to the iac directory
cd iac
# Update the version
.\update-version.ps1
```
### Building Docker Images
#### Using the build scripts
On Linux/macOS:
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Make the script executable
chmod +x build.sh
# Build the image with version from version.txt
./build.sh
# Build with custom tag
./build.sh --tag 2025.05.21.1430
# Build and push to a registry
./build.sh --registry your-registry.com --push
```
On Windows:
```powershell
# Navigate to the iac/docker directory
cd iac\docker
# Build the image with version from version.txt
.\build.ps1
# Build with custom tag
.\build.ps1 -tag 2025.05.21.1430
# Build and push to a registry
.\build.ps1 -registry your-registry.com -push
```
#### Manually
```bash
# Navigate to the project root
cd /path/to/project
# Get the version from version.txt
VERSION=$(cat iac/version.txt)
# Build the image
docker build -t ActiveDirectoryManager:$VERSION -f iac/docker/Dockerfile .
# Tag the image for a registry
docker tag ActiveDirectoryManager:$VERSION your-registry.com/ActiveDirectoryManager:$VERSION
# Push to registry
docker push your-registry.com/ActiveDirectoryManager:$VERSION
```
### Running with Docker Compose
```bash
# Navigate to the iac/docker directory
cd iac/docker
# Create a .env file from the example if you haven't already
cp .env.example .env
# Edit the .env file with your configuration
# Load the version from version.txt
source .env.version
# Start the application stack
docker-compose up -d
# View logs
docker-compose logs -f
# Stop the application stack
docker-compose down
```
## Kubernetes
### Prerequisites
- Kubernetes cluster (v1.19+)
- kubectl configured to communicate with your cluster
- Kustomize (v4.0+) or kubectl v1.14+ which includes kustomize
### Updating Deployment Version
Before deploying, you should update the Kubernetes deployment to use the current version:
On Linux/macOS:
```bash
# Navigate to the iac/kubernetes directory
cd iac/kubernetes
# Make the script executable
chmod +x update-deployment-version.sh
# Update the deployment version
./update-deployment-version.sh
```
On Windows:
```powershell
# Navigate to the iac/kubernetes directory
cd iac\kubernetes
# Update the deployment version
.\update-deployment-version.ps1
```
### Deployment
```bash
# Navigate to the iac/kubernetes directory
cd iac/kubernetes
# Apply all resources
kubectl apply -k .
# Or from anywhere in the project
kubectl apply -k iac/kubernetes
```
### Customization
To customize the deployment for different environments:
1. Create a new directory for your environment:
```bash
mkdir -p iac/kubernetes/environments/production
```
2. Create a kustomization.yaml file that references the base configuration:
```yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: ad-management-production
resources:
- ../../ # Reference the base configuration
patches:
# Add your patches here
```
3. Apply the environment-specific configuration:
```bash
kubectl apply -k iac/kubernetes/environments/production
```
## CI/CD Integration
The Docker build scripts are designed to be used in CI/CD pipelines. Example integration with GitHub Actions:
```yaml
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build and push Docker image
run: |
cd iac/docker
chmod +x build.sh
./build.sh --registry ghcr.io --tag ${{ github.sha }} --push
```
+21
View File
@@ -0,0 +1,21 @@
# Environment Variables for Docker Deployment
# Copy from .env.example and customize as needed
# Database Configuration
POSTGRES_USER=admanagement
POSTGRES_PASSWORD=change_this_to_a_secure_password
POSTGRES_DB=admanagement
# Application Configuration
NODE_ENV=production
PORT=5000
BASE_URL=http://localhost:5000
SESSION_SECRET=change_this_to_a_random_string
JWT_SECRET=change_this_to_a_different_random_string
# Default Admin User Configuration
DEFAULT_ADMIN_USERNAME=admin
DEFAULT_ADMIN_PASSWORD=change_this_to_a_secure_password
DEFAULT_ADMIN_EMAIL=admin@example.com
DEFAULT_ADMIN_FULLNAME=System Administrator
DISABLE_REGISTRATION=false
+1 -1
View File
@@ -53,4 +53,4 @@ DISABLE_REGISTRATION=false
# OIDC_USERINFO_URL=https://openidconnect.googleapis.com/v1/userinfo
# OIDC_CLIENT_ID=your_client_id
# OIDC_CLIENT_SECRET=your_client_secret
# OIDC_CALLBACK_URL=http://localhost:5000/api/auth/oidc/callback
# OIDC_CALLBACK_URL=http://localhost:5000/api/auth/oidc/callback
@@ -22,4 +22,4 @@ SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTPONLY=true
# API Settings
SWAGGER_ENABLED=true
SWAGGER_ENABLED=true
+3
View File
@@ -0,0 +1,3 @@
# This file is used to set the VERSION environment variable for docker-compose
# It reads the version from the version.txt file
VERSION=$(cat ../version.txt)
+1 -1
View File
@@ -62,4 +62,4 @@ USER appuser
EXPOSE 5000
# Start the application
CMD ["node", "dist/server/index.js"]
CMD ["node", "dist/server/index.js"]
+74
View File
@@ -0,0 +1,74 @@
# PowerShell script for building Docker images (Windows)
param (
[string]$tag = "",
[string]$registry = "",
[switch]$push = $false,
[string]$imageName = "ActiveDirectoryManager"
)
# If no tag is provided, use the version from version.txt
if (-not $tag) {
$versionFile = Join-Path (Split-Path $PSScriptRoot) "version.txt"
if (Test-Path $versionFile) {
$tag = Get-Content $versionFile -Raw
$tag = $tag.Trim()
} else {
# If version.txt doesn't exist, use current date/time in yyyy.MM.dd.HHmm format
$tag = Get-Date -Format "yyyy.MM.dd.HHmm"
}
}
# Set error action preference to stop on any error
$ErrorActionPreference = "Stop"
# Display build information
Write-Host "Building Docker image: $imageName" -ForegroundColor Green
Write-Host "Tag: $tag" -ForegroundColor Green
if ($registry) {
Write-Host "Registry: $registry" -ForegroundColor Green
}
# Determine the full image name
$fullImageName = if ($registry) { "$registry/$imageName" } else { $imageName }
# Check if Docker is available
try {
docker --version
}
catch {
Write-Host "Docker is not available. Please install Docker and try again." -ForegroundColor Red
exit 1
}
# Build the Docker image
try {
Write-Host "Building image: $fullImageName`:$tag" -ForegroundColor Cyan
# Navigate to the root directory (two levels up from the script location)
$rootDir = (Get-Item $PSScriptRoot).Parent.Parent.FullName
# Build the Docker image
docker build -t "$fullImageName`:$tag" -f "$PSScriptRoot/Dockerfile" $rootDir
if ($LASTEXITCODE -ne 0) {
throw "Docker build failed with exit code $LASTEXITCODE"
}
Write-Host "Successfully built image: $fullImageName`:$tag" -ForegroundColor Green
# Push the image if requested
if ($push) {
Write-Host "Pushing image to registry: $fullImageName`:$tag" -ForegroundColor Cyan
docker push "$fullImageName`:$tag"
if ($LASTEXITCODE -ne 0) {
throw "Docker push failed with exit code $LASTEXITCODE"
}
Write-Host "Successfully pushed image: $fullImageName`:$tag" -ForegroundColor Green
}
}
catch {
Write-Host "Error: $_" -ForegroundColor Red
exit 1
}
+91
View File
@@ -0,0 +1,91 @@
#!/bin/bash
# Bash script for building Docker images (Linux/macOS)
# Default values
TAG=""
REGISTRY=""
PUSH=false
IMAGE_NAME="ActiveDirectoryManager"
# If no tag is provided, use the version from version.txt
if [ -z "$TAG" ]; then
VERSION_FILE="$(dirname "$(dirname "$0")")/version.txt"
if [ -f "$VERSION_FILE" ]; then
TAG=$(cat "$VERSION_FILE" | tr -d '[:space:]')
else
# If version.txt doesn't exist, use current date/time in yyyy.MM.dd.HHmm format
TAG=$(date +"%Y.%m.%d.%H%M")
fi
fi
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
--tag)
TAG="$2"
shift 2
;;
--registry)
REGISTRY="$2"
shift 2
;;
--push)
PUSH=true
shift
;;
--image-name)
IMAGE_NAME="$2"
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Determine the full image name
if [ -n "$REGISTRY" ]; then
FULL_IMAGE_NAME="$REGISTRY/$IMAGE_NAME"
else
FULL_IMAGE_NAME="$IMAGE_NAME"
fi
# Display build information
echo -e "\e[32mBuilding Docker image: $IMAGE_NAME\e[0m"
echo -e "\e[32mTag: $TAG\e[0m"
if [ -n "$REGISTRY" ]; then
echo -e "\e[32mRegistry: $REGISTRY\e[0m"
fi
# Check if Docker is available
if ! command -v docker &> /dev/null; then
echo -e "\e[31mDocker is not available. Please install Docker and try again.\e[0m"
exit 1
fi
# Build the Docker image
echo -e "\e[36mBuilding image: $FULL_IMAGE_NAME:$TAG\e[0m"
# Navigate to the root directory (two levels up from the script location)
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
# Build the Docker image
if ! docker build -t "$FULL_IMAGE_NAME:$TAG" -f "$(dirname "${BASH_SOURCE[0]}")/Dockerfile" "$ROOT_DIR"; then
echo -e "\e[31mDocker build failed\e[0m"
exit 1
fi
echo -e "\e[32mSuccessfully built image: $FULL_IMAGE_NAME:$TAG\e[0m"
# Push the image if requested
if [ "$PUSH" = true ]; then
echo -e "\e[36mPushing image to registry: $FULL_IMAGE_NAME:$TAG\e[0m"
if ! docker push "$FULL_IMAGE_NAME:$TAG"; then
echo -e "\e[31mDocker push failed\e[0m"
exit 1
fi
echo -e "\e[32mSuccessfully pushed image: $FULL_IMAGE_NAME:$TAG\e[0m"
fi
@@ -1,9 +1,10 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
container_name: ad-management-api
context: ../../
dockerfile: iac/docker/Dockerfile
image: ActiveDirectoryManager:${VERSION:-latest}
container_name: ActiveDirectoryManager-api
restart: unless-stopped
ports:
- "5000:5000"
@@ -34,7 +35,7 @@ services:
postgres:
image: postgres:15-alpine
container_name: ad-management-postgres
container_name: ActiveDirectoryManager-postgres
restart: unless-stopped
environment:
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
@@ -55,7 +56,7 @@ services:
redis:
image: redis:7-alpine
container_name: ad-management-redis
container_name: ActiveDirectoryManager-redis
restart: unless-stopped
command: redis-server --appendonly yes
volumes:
@@ -76,4 +77,4 @@ networks:
volumes:
postgres-data:
redis-data:
redis-data:
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: ad-management-config
namespace: ad-management
data:
NODE_ENV: "production"
PORT: "5000"
BASE_URL: "https://ad-management.example.com"
DEFAULT_ADMIN_USERNAME: "admin"
DEFAULT_ADMIN_FULLNAME: "System Administrator"
DISABLE_REGISTRATION: "false"
POSTGRES_DB: "admgr"
POSTGRES_USER: "postgres"
+73
View File
@@ -0,0 +1,73 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: ad-management
namespace: ad-management
spec:
replicas: 2
selector:
matchLabels:
app: ad-management
template:
metadata:
labels:
app: ad-management
spec:
containers:
- name: activedirectorymanager
image: ActiveDirectoryManager:2025.05.21.1430
imagePullPolicy: Always
ports:
- containerPort: 5000
name: http
envFrom:
- configMapRef:
name: ad-management-config
- secretRef:
name: ad-management-secrets
env:
- name: REDIS_HOST
value: "redis"
- name: REDIS_PORT
value: "6379"
- name: POSTGRES_HOST
value: "postgres"
- name: POSTGRES_PORT
value: "5432"
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "300m"
livenessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /api/health
port: http
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 1
---
apiVersion: v1
kind: Service
metadata:
name: ad-management
namespace: ad-management
spec:
selector:
app: ad-management
ports:
- port: 80
targetPort: 5000
name: http
type: ClusterIP
+26
View File
@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ad-management-ingress
namespace: ad-management
annotations:
kubernetes.io/ingress.class: "nginx"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
tls:
- hosts:
- ad-management.example.com
secretName: ad-management-tls
rules:
- host: ad-management.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: ad-management
port:
name: http
+13
View File
@@ -0,0 +1,13 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: ad-management
resources:
- namespace.yaml
- configmap.yaml
- secrets.yaml
- postgres.yaml
- redis.yaml
- deployment.yaml
- ingress.yaml
+6
View File
@@ -0,0 +1,6 @@
apiVersion: v1
kind: Namespace
metadata:
name: ad-management
labels:
name: ad-management
+89
View File
@@ -0,0 +1,89 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: ad-management
spec:
serviceName: postgres
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:15-alpine
ports:
- containerPort: 5432
name: postgres
env:
- name: POSTGRES_DB
valueFrom:
configMapKeyRef:
name: ad-management-config
key: POSTGRES_DB
- name: POSTGRES_USER
valueFrom:
configMapKeyRef:
name: ad-management-config
key: POSTGRES_USER
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: ad-management-secrets
key: POSTGRES_PASSWORD
volumeMounts:
- name: postgres-data
mountPath: /var/lib/postgresql/data
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "500m"
livenessProbe:
exec:
command:
- pg_isready
- -U
- postgres
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
exec:
command:
- pg_isready
- -U
- postgres
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 1
volumeClaimTemplates:
- metadata:
name: postgres-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: ad-management
spec:
selector:
app: postgres
ports:
- port: 5432
targetPort: 5432
clusterIP: None # Headless service for StatefulSet
+71
View File
@@ -0,0 +1,71 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: ad-management
spec:
serviceName: redis
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
ports:
- containerPort: 6379
name: redis
volumeMounts:
- name: redis-data
mountPath: /data
resources:
requests:
memory: "128Mi"
cpu: "50m"
limits:
memory: "256Mi"
cpu: "200m"
livenessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
exec:
command:
- redis-cli
- ping
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 1
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: ad-management
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
clusterIP: None # Headless service for StatefulSet
+14
View File
@@ -0,0 +1,14 @@
apiVersion: v1
kind: Secret
metadata:
name: ad-management-secrets
namespace: ad-management
type: Opaque
data:
# These are example values. In production, replace with your own base64-encoded secrets
# Example: echo -n "your-secret-value" | base64
JWT_SECRET: Y2hhbmdlLXRoaXMtaW4tcHJvZHVjdGlvbg==
SESSION_SECRET: Y2hhbmdlLXRoaXMtaW4tcHJvZHVjdGlvbg==
DEFAULT_ADMIN_PASSWORD: cGFzc3dvcmQ=
DEFAULT_ADMIN_EMAIL: YWRtaW5AZXhhbXBsZS5jb20=
POSTGRES_PASSWORD: cG9zdGdyZXM=
@@ -0,0 +1,40 @@
# PowerShell script to update the Kubernetes deployment with the current version
# Get the directory of this script
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$parentDir = Split-Path -Parent $scriptDir
# Read the version from version.txt
$versionFile = Join-Path $parentDir "version.txt"
if (-not (Test-Path $versionFile)) {
Write-Host "Error: version.txt not found at $versionFile" -ForegroundColor Red
exit 1
}
$version = Get-Content $versionFile -Raw
$version = $version.Trim()
Write-Host "Using version: $version" -ForegroundColor Cyan
# Update the deployment.yaml file
$deploymentFile = Join-Path $scriptDir "deployment.yaml"
if (-not (Test-Path $deploymentFile)) {
Write-Host "Error: deployment.yaml not found at $deploymentFile" -ForegroundColor Red
exit 1
}
# Read the deployment file
$content = Get-Content $deploymentFile -Raw
# Replace the image version
$pattern1 = 'image: ActiveDirectoryManager:[0-9]{4}\.[0-9]{2}\.[0-9]{2}\.[0-9]{4}'
$replacement1 = "image: ActiveDirectoryManager:$version"
$content = $content -replace $pattern1, $replacement1
$pattern2 = 'image: ActiveDirectoryManager:latest'
$replacement2 = "image: ActiveDirectoryManager:$version"
$content = $content -replace $pattern2, $replacement2
# Write the updated content back to the file
$content | Set-Content $deploymentFile
Write-Host "Updated deployment.yaml with version $version" -ForegroundColor Green
@@ -0,0 +1,36 @@
#!/bin/bash
# Script to update the Kubernetes deployment with the current version
# Get the directory of this script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
# Read the version from version.txt
VERSION_FILE="$PARENT_DIR/version.txt"
if [ ! -f "$VERSION_FILE" ]; then
echo "Error: version.txt not found at $VERSION_FILE"
exit 1
fi
VERSION=$(cat "$VERSION_FILE" | tr -d '[:space:]')
echo "Using version: $VERSION"
# Update the deployment.yaml file
DEPLOYMENT_FILE="$SCRIPT_DIR/deployment.yaml"
if [ ! -f "$DEPLOYMENT_FILE" ]; then
echo "Error: deployment.yaml not found at $DEPLOYMENT_FILE"
exit 1
fi
# Use sed to replace the image version
if [[ "$OSTYPE" == "darwin"* ]]; then
# macOS requires a different sed syntax
sed -i '' "s|image: ActiveDirectoryManager:[0-9]\{4\}\.[0-9]\{2\}\.[0-9]\{2\}\.[0-9]\{4\}|image: ActiveDirectoryManager:$VERSION|g" "$DEPLOYMENT_FILE"
sed -i '' "s|image: ActiveDirectoryManager:latest|image: ActiveDirectoryManager:$VERSION|g" "$DEPLOYMENT_FILE"
else
# Linux
sed -i "s|image: ActiveDirectoryManager:[0-9]\{4\}\.[0-9]\{2\}\.[0-9]\{2\}\.[0-9]\{4\}|image: ActiveDirectoryManager:$VERSION|g" "$DEPLOYMENT_FILE"
sed -i "s|image: ActiveDirectoryManager:latest|image: ActiveDirectoryManager:$VERSION|g" "$DEPLOYMENT_FILE"
fi
echo "Updated deployment.yaml with version $VERSION"
+24
View File
@@ -0,0 +1,24 @@
# PowerShell script to update version and apply it to all components
# Get the directory of this script
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
# Update version.txt with current date/time
Write-Host "Updating version.txt..." -ForegroundColor Cyan
& "$scriptDir\update-version.ps1"
# Read the new version
$version = Get-Content "$scriptDir\version.txt" -Raw
$version = $version.Trim()
Write-Host "New version: $version" -ForegroundColor Green
# Update Kubernetes deployment
Write-Host "Updating Kubernetes deployment..." -ForegroundColor Cyan
& "$scriptDir\kubernetes\update-deployment-version.ps1"
Write-Host "Version update complete. New version: $version" -ForegroundColor Green
Write-Host ""
Write-Host "Next steps:" -ForegroundColor Yellow
Write-Host "1. Build Docker image: cd $scriptDir\docker; .\build.ps1" -ForegroundColor Yellow
Write-Host "2. Push Docker image: cd $scriptDir\docker; .\build.ps1 -push" -ForegroundColor Yellow
Write-Host "3. Deploy to Kubernetes: cd $scriptDir\kubernetes; kubectl apply -k ." -ForegroundColor Yellow
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
# Comprehensive script to update version and apply it to all components
# Get the directory of this script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Update version.txt with current date/time
echo "Updating version.txt..."
"$SCRIPT_DIR/update-version.sh"
# Read the new version
VERSION=$(cat "$SCRIPT_DIR/version.txt" | tr -d '[:space:]')
echo "New version: $VERSION"
# Update Kubernetes deployment
echo "Updating Kubernetes deployment..."
"$SCRIPT_DIR/kubernetes/update-deployment-version.sh"
echo "Version update complete. New version: $VERSION"
echo ""
echo "Next steps:"
echo "1. Build Docker image: cd $SCRIPT_DIR/docker && ./build.sh"
echo "2. Push Docker image: cd $SCRIPT_DIR/docker && ./build.sh --push"
echo "3. Deploy to Kubernetes: cd $SCRIPT_DIR/kubernetes && kubectl apply -k ."
+10
View File
@@ -0,0 +1,10 @@
# PowerShell script to update the version.txt file with the current date and time
# Generate version in yyyy.MM.dd.HHmm format
$version = Get-Date -Format "yyyy.MM.dd.HHmm"
# Update version.txt
$versionFile = Join-Path $PSScriptRoot "version.txt"
$version | Out-File -FilePath $versionFile -NoNewline
Write-Host "Version updated to: $version"
+10
View File
@@ -0,0 +1,10 @@
#!/bin/bash
# Script to update the version.txt file with the current date and time
# Generate version in yyyy.MM.dd.HHmm format
VERSION=$(date +"%Y.%m.%d.%H%M")
# Update version.txt
echo "$VERSION" > "$(dirname "$0")/version.txt"
echo "Version updated to: $VERSION"
+1
View File
@@ -0,0 +1 @@
2025.05.21.1430
+1177 -206
View File
File diff suppressed because it is too large Load Diff
+1630 -536
View File
File diff suppressed because it is too large Load Diff
+95 -16
View File
@@ -35,6 +35,32 @@ const swaggerOptions = {
description: "API base URL",
},
],
tags: [
{
name: "LDAP Connections",
description: "Operations for managing LDAP connections"
},
{
name: "AD Users",
description: "Operations for managing Active Directory users"
},
{
name: "AD Groups",
description: "Operations for managing Active Directory groups"
},
{
name: "AD Computers",
description: "Operations for managing Active Directory computers"
},
{
name: "AD Organizational Units",
description: "Operations for managing Active Directory organizational units"
},
{
name: "Active Directory",
description: "General Active Directory operations including password management and account status"
}
],
components: {
securitySchemes: {
bearerAuth: {
@@ -299,7 +325,7 @@ const swaggerOptions = {
id: { type: "integer" },
action: { type: "string" },
targetId: { type: "string" },
details: {
details: {
type: "object",
additionalProperties: true
},
@@ -308,6 +334,59 @@ const swaggerOptions = {
connectionId: { type: "integer" }
},
},
PasswordReset: {
type: "object",
required: ["userObjectGUID", "newPassword"],
properties: {
userObjectGUID: {
type: "string",
description: "The ObjectGUID of the user"
},
newPassword: {
type: "string",
description: "The new password for the user"
},
skipValidation: {
type: "boolean",
description: "Whether to skip password policy validation",
default: false
},
requirePasswordChangeAtNextLogon: {
type: "boolean",
description: "Whether to require the user to change their password at next logon",
default: true
}
}
},
EnableUserAccount: {
type: "object",
required: ["userObjectGUID", "enabled"],
properties: {
userObjectGUID: {
type: "string",
description: "The ObjectGUID of the user"
},
enabled: {
type: "boolean",
description: "Whether to enable or disable the account"
}
}
},
BulkEnableUserAccounts: {
type: "object",
required: ["userDNs", "enabled"],
properties: {
userDNs: {
type: "array",
items: { type: "string" },
description: "Array of user distinguished names"
},
enabled: {
type: "boolean",
description: "Whether to enable or disable the accounts"
}
}
},
},
responses: {
UnauthorizedError: {
@@ -394,13 +473,13 @@ export function setupSwagger(app: Express) {
// Mount at both paths for backward compatibility
app.use("/api/docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, swaggerUiOptions));
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(swaggerSpec, swaggerUiOptions));
// Provide the JSON spec at multiple paths
app.get("/api/swagger.json", (req, res) => {
res.setHeader("Content-Type", "application/json");
res.send(swaggerSpec);
});
app.get("/api-docs/swagger.json", (req, res) => {
res.setHeader("Content-Type", "application/json");
res.send(swaggerSpec);
@@ -408,8 +487,8 @@ export function setupSwagger(app: Express) {
// Add download endpoint for the OpenAPI specification
app.get("/api/docs/download", (req, res) => {
const format = req.query.format?.toString().toLowerCase() || 'json';
const format = req.query.format?.toString().toLowerCase() ?? 'json';
if (format === 'json') {
res.setHeader('Content-Disposition', 'attachment; filename=openapi-spec.json');
res.setHeader('Content-Type', 'application/json');
@@ -455,7 +534,7 @@ export function setupSwagger(app: Express) {
</head>
<body>
<h1>Active Directory Management API - Advanced Usage Guide</h1>
<h2>API Documentation</h2>
<p>The complete API documentation is available at <a href="/api/docs">/api/docs</a>.</p>
<p>You can also download the OpenAPI specification:</p>
@@ -463,28 +542,28 @@ export function setupSwagger(app: Express) {
<a href="/api/docs/download?format=json" class="btn">Download OpenAPI Spec (JSON)</a>
<a href="/api/docs/download?format=yaml" class="btn">Download OpenAPI Spec (YAML)</a>
</p>
<h2>Query Parameters and Filtering</h2>
<pre>${queryParamsInfo}</pre>
<h2>Authentication</h2>
<p>This API supports two authentication methods:</p>
<ul>
<li><strong>Session-based authentication</strong>: Used when accessing the API from the web interface.</li>
<li><strong>API Token authentication</strong>: Used when accessing the API programmatically.</li>
</ul>
<h3>API Token Authentication</h3>
<p>To authenticate using an API token, include the token in the Authorization header:</p>
<pre>Authorization: Bearer YOUR_API_TOKEN</pre>
<h2>Rate Limiting</h2>
<p>The API has rate limiting in place to prevent abuse. The current limits are:</p>
<ul>
<li>100 requests per minute for authenticated users</li>
<li>20 requests per minute for unauthenticated users</li>
</ul>
<h2>Error Handling</h2>
<p>The API returns consistent error responses with the following structure:</p>
<pre>{
@@ -496,20 +575,20 @@ export function setupSwagger(app: Express) {
}
]
}</pre>
<h2>Example Usage</h2>
<h3>Filter Users by Name</h3>
<pre>GET /api/connections/1/ad-users?filter=displayName contains 'John'</pre>
<h3>Select Specific Fields</h3>
<pre>GET /api/connections/1/ad-users?select=id,displayName,email</pre>
<h3>Pagination</h3>
<pre>GET /api/connections/1/ad-users?top=10&skip=20</pre>
<h3>Combining Parameters</h3>
<pre>GET /api/connections/1/ad-users?filter=enabled eq true&select=id,displayName,email&orderBy=displayName asc&top=10</pre>
<p>Return to <a href="/api/docs">API Documentation</a></p>
</body>
</html>
+77 -27
View File
@@ -19,48 +19,48 @@ export const PERMISSIONS = {
CREATE_USERS: "create:users",
UPDATE_USERS: "update:users",
DELETE_USERS: "delete:users",
// LDAP connections
VIEW_LDAP_CONNECTIONS: "view:ldap_connections",
CREATE_LDAP_CONNECTIONS: "create:ldap_connections",
UPDATE_LDAP_CONNECTIONS: "update:ldap_connections",
DELETE_LDAP_CONNECTIONS: "delete:ldap_connections",
// Active Directory management
VIEW_AD_USERS: "view:ad_users",
CREATE_AD_USERS: "create:ad_users",
UPDATE_AD_USERS: "update:ad_users",
DELETE_AD_USERS: "delete:ad_users",
MOVE_AD_USERS: "move:ad_users",
VIEW_AD_GROUPS: "view:ad_groups",
CREATE_AD_GROUPS: "create:ad_groups",
UPDATE_AD_GROUPS: "update:ad_groups",
DELETE_AD_GROUPS: "delete:ad_groups",
MANAGE_GROUP_MEMBERSHIP: "manage:group_membership",
VIEW_AD_OUS: "view:ad_ous",
CREATE_AD_OUS: "create:ad_ous",
UPDATE_AD_OUS: "update:ad_ous",
DELETE_AD_OUS: "delete:ad_ous",
VIEW_AD_COMPUTERS: "view:ad_computers",
CREATE_AD_COMPUTERS: "create:ad_computers",
UPDATE_AD_COMPUTERS: "update:ad_computers",
DELETE_AD_COMPUTERS: "delete:ad_computers",
MOVE_AD_COMPUTERS: "move:ad_computers",
VIEW_AD_DOMAINS: "view:ad_domains",
CREATE_AD_DOMAINS: "create:ad_domains",
UPDATE_AD_DOMAINS: "update:ad_domains",
DELETE_AD_DOMAINS: "delete:ad_domains",
// Sites and Services management
VIEW_AD_SITES: "view:ad_sites",
CREATE_AD_SITES: "create:ad_sites",
UPDATE_AD_SITES: "update:ad_sites",
DELETE_AD_SITES: "delete:ad_sites",
VIEW_AD_SUBNETS: "view:ad_subnets",
CREATE_AD_SUBNETS: "create:ad_subnets",
UPDATE_AD_SUBNETS: "update:ad_subnets",
@@ -68,7 +68,7 @@ export const PERMISSIONS = {
// API Token management
MANAGE_API_TOKENS: "manage:api_tokens",
// Administrative functions
MANAGE_ROLES: "manage:roles",
SYSTEM_ADMIN: "admin:system",
@@ -175,10 +175,10 @@ export const ldapConnections = pgTable("ldap_connections", {
export const scheduleFrequencies = [
"once",
"minutely",
"hourly",
"daily",
"weekly",
"monthly",
"hourly",
"daily",
"weekly",
"monthly",
"yearly"
] as const;
@@ -234,7 +234,7 @@ export const dynamicGroupRules = pgTable("dynamic_group_rules", {
// Rule conditions (filter logic)
export const dynamicGroupConditions = pgTable("dynamic_group_conditions", {
id: serial("id").primaryKey(),
id: serial("id").primaryKey(),
ruleId: integer("rule_id").references(() => dynamicGroupRules.id, { onDelete: "cascade" }).notNull(),
parentId: integer("parent_id").references(() => dynamicGroupConditions.id, { onDelete: "cascade" }),
type: text("type").notNull(), // "condition", "group"
@@ -498,6 +498,53 @@ export const removeFromGroupSchema = z.object({
objectType: z.enum(["user", "computer"]),
});
// Password management schemas
export const resetPasswordSchema = z.object({
userObjectGUID: z.string().min(1, "User ObjectGUID is required"),
newPassword: z.string().min(8, "Password must be at least 8 characters long"),
skipValidation: z.boolean().optional().default(false),
requirePasswordChangeAtNextLogon: z.boolean().optional().default(true),
});
export const enableUserAccountSchema = z.object({
userObjectGUID: z.string().min(1, "User ObjectGUID is required"),
enabled: z.boolean(),
});
// Bulk operations schemas
export const bulkEnableUserAccountsSchema = z.object({
userDNs: z.array(z.string().min(1, "User DN is required")),
enabled: z.boolean(),
});
// Bulk operations schemas
export const bulkMoveObjectsSchema = z.object({
objectDNs: z.array(z.string().min(1, "Object DN is required")),
targetOU: z.string().min(1, "Target OU is required"),
});
export const bulkAddToGroupSchema = z.object({
memberDNs: z.array(z.string().min(1, "Member DN is required")),
groupDN: z.string().min(1, "Group DN is required"),
});
export const bulkRemoveFromGroupSchema = z.object({
memberDNs: z.array(z.string().min(1, "Member DN is required")),
groupDN: z.string().min(1, "Group DN is required"),
});
export const bulkEnableUserAccountsSchema = z.object({
userDNs: z.array(z.string().min(1, "User DN is required")),
enabled: z.boolean(),
});
export const bulkUpdateAttributeSchema = z.object({
objectDNs: z.array(z.string().min(1, "Object DN is required")),
attributeName: z.string().min(1, "Attribute name is required"),
attributeValue: z.union([z.string(), z.array(z.string()), z.null()]),
operation: z.enum(["add", "replace", "delete"]).default("replace"),
});
// ManagedBy operation schema is no longer needed as it's been replaced by object-specific PATCH endpoints
// Export types
@@ -536,6 +583,9 @@ export type MoveComputer = z.infer<typeof moveComputerSchema>;
export type MoveUser = z.infer<typeof moveUserSchema>;
export type AddToGroup = z.infer<typeof addToGroupSchema>;
export type RemoveFromGroup = z.infer<typeof removeFromGroupSchema>;
export type ResetPassword = z.infer<typeof resetPasswordSchema>;
export type EnableUserAccount = z.infer<typeof enableUserAccountSchema>;
export type BulkEnableUserAccounts = z.infer<typeof bulkEnableUserAccountsSchema>;
export type DynamicGroupRule = typeof dynamicGroupRules.$inferSelect;
export type InsertDynamicGroupRule = z.infer<typeof insertDynamicGroupRuleSchema>;
export type DynamicGroupCondition = typeof dynamicGroupConditions.$inferSelect;
@@ -651,22 +701,22 @@ export const ldapAttributesRelations = relations(ldapAttributes, ({ one }) => ({
}));
// Generate insertion schemas
export const insertLdapFilterSchema = createInsertSchema(ldapFilters).omit({
id: true,
createdAt: true,
export const insertLdapFilterSchema = createInsertSchema(ldapFilters).omit({
id: true,
createdAt: true,
modifiedAt: true,
currentVersion: true
currentVersion: true
});
export const insertLdapFilterRevisionSchema = createInsertSchema(ldapFilterRevisions).omit({
id: true,
createdAt: true
export const insertLdapFilterRevisionSchema = createInsertSchema(ldapFilterRevisions).omit({
id: true,
createdAt: true
});
export const insertLdapAttributeSchema = createInsertSchema(ldapAttributes).omit({
id: true,
createdAt: true,
updatedAt: true
export const insertLdapAttributeSchema = createInsertSchema(ldapAttributes).omit({
id: true,
createdAt: true,
updatedAt: true
});
// Export types
@@ -678,8 +728,8 @@ export type LdapAttribute = typeof ldapAttributes.$inferSelect;
export type InsertLdapAttribute = z.infer<typeof insertLdapAttributeSchema>;
// Audit log schema and types
export const insertAuditLogSchema = createInsertSchema(auditLogs).omit({
id: true,
export const insertAuditLogSchema = createInsertSchema(auditLogs).omit({
id: true,
timestamp: true
});
export type AuditLog = typeof auditLogs.$inferSelect;