diff --git a/.github/workflows/build_and_release_for_big_bear_casaos_user_management.yaml b/.github/workflows/build_and_release_for_big_bear_casaos_user_management.yaml
new file mode 100644
index 0000000..6d37acb
--- /dev/null
+++ b/.github/workflows/build_and_release_for_big_bear_casaos_user_management.yaml
@@ -0,0 +1,47 @@
+name: "Build and release for big-bear-casaos-user-management"
+
+on:
+ push:
+ branches:
+ - main
+ paths:
+ - "big-bear-casaos-user-management/**"
+
+jobs:
+ create:
+ name: "Creates the newest release by version"
+ runs-on: "ubuntu-latest"
+
+ steps:
+ - name: Checkout project
+ uses: actions/checkout@v2.3.4
+
+ # New step to read the VERSION file and set the version as an output
+ - name: Get the version
+ id: get_version
+ run: echo "big_bear_casaos_user_management_version=$(cat big-bear-casaos-user-management/VERSION)" >> $GITHUB_ENV
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@master
+ with:
+ platforms: all
+
+ - name: Set up Docker Build
+ uses: docker/setup-buildx-action@v3
+
+ - name: Login to DockerHub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Build and push
+ uses: docker/build-push-action@v3
+ with:
+ push: true
+ platforms: linux/amd64,linux/arm64
+ context: ./big-bear-casaos-user-management
+ file: ./big-bear-casaos-user-management/Dockerfile
+ tags: |
+ bigbeartechworld/big-bear-casaos-user-management:latest
+ bigbeartechworld/big-bear-casaos-user-management:${{ env.big_bear_casaos_user_management_version }}
diff --git a/.gitignore b/.gitignore
index 9804a16..ed1ad03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,7 @@ pihole-unbound/build.log
pihole-unbound/build.sh
genmon/build.log
genmon/build.sh
+genmon/build.log
+genmon/build.sh
+big-bear-casaos-user-management/build.log
+big-bear-casaos-user-management/build.sh
\ No newline at end of file
diff --git a/big-bear-casaos-user-management/Dockerfile b/big-bear-casaos-user-management/Dockerfile
new file mode 100644
index 0000000..18dcf4f
--- /dev/null
+++ b/big-bear-casaos-user-management/Dockerfile
@@ -0,0 +1,27 @@
+FROM python:3.9-slim
+
+# Install system dependencies (systemd includes systemctl)
+RUN apt-get update && \
+ apt-get install -y systemd sudo && \
+ rm -rf /var/lib/apt/lists/*
+
+# Create necessary directories
+RUN mkdir -p /var/lib/casaos/db
+
+# Set working directory
+WORKDIR /app
+
+# Copy all application files
+COPY app/ .
+
+# Install Python dependencies
+RUN pip install flask flask-wtf
+
+# Create a volume for persistent storage
+VOLUME /var/lib/casaos/db
+
+# Expose port for Flask
+EXPOSE 5000
+
+# Run Flask app
+CMD ["python", "app.py"]
diff --git a/big-bear-casaos-user-management/VERSION b/big-bear-casaos-user-management/VERSION
new file mode 100644
index 0000000..8a9ecc2
--- /dev/null
+++ b/big-bear-casaos-user-management/VERSION
@@ -0,0 +1 @@
+0.0.1
\ No newline at end of file
diff --git a/big-bear-casaos-user-management/app/app.py b/big-bear-casaos-user-management/app/app.py
new file mode 100644
index 0000000..9c20912
--- /dev/null
+++ b/big-bear-casaos-user-management/app/app.py
@@ -0,0 +1,71 @@
+from flask import Flask, render_template, redirect, url_for, flash, request
+from user_management import (list_users, add_user, edit_password,
+ remove_user, reset_database, hash_password)
+from functools import wraps
+from flask import request, Response
+import os
+
+app = Flask(__name__)
+app.config['SECRET_KEY'] = 'your-secret-key'
+
+ADMIN_USERNAME = os.environ.get('ADMIN_USERNAME', 'admin')
+ADMIN_PASSWORD = os.environ.get('ADMIN_PASSWORD', 'YOUR_SECURE_PASSWORD')
+
+def check_auth(username, password):
+ """Verify admin credentials"""
+ return username == ADMIN_USERNAME and password == ADMIN_PASSWORD
+
+def authenticate():
+ return Response(
+ 'Could not verify your access level for that URL.\n'
+ 'You have to login with proper credentials', 401,
+ {'WWW-Authenticate': 'Basic realm="Login Required"'}
+ )
+
+def requires_auth(f):
+ @wraps(f)
+ def decorated(*args, **kwargs):
+ auth = request.authorization
+ if not auth or not check_auth(auth.username, auth.password):
+ return authenticate()
+ return f(*args, **kwargs)
+ return decorated
+
+@app.route('/')
+@requires_auth
+def index():
+ users = list_users(return_data=True)
+ return render_template('index.html', users=users)
+
+@app.route('/add_user', methods=['POST'])
+def add_user_route():
+ username = request.form.get('username')
+ password = request.form.get('password')
+ if add_user(username, password):
+ flash('User added successfully', 'success')
+ return redirect(url_for('index'))
+
+@app.route('/edit_password', methods=['POST'])
+def edit_password_route():
+ user_id = request.form.get('user_id')
+ new_password = request.form.get('new_password')
+ if edit_password(user_id, new_password):
+ flash('Password updated successfully', 'success')
+ return redirect(url_for('index'))
+
+@app.route('/remove_user', methods=['POST'])
+def remove_user_route():
+ user_id = request.form.get('user_id')
+ if remove_user(user_id):
+ flash('User removed successfully', 'success')
+ return redirect(url_for('index'))
+
+@app.route('/reset_database', methods=['POST'])
+@requires_auth
+def reset_database_route():
+ if reset_database():
+ flash('Database reset successfully', 'success')
+ return redirect(url_for('index'))
+
+if __name__ == '__main__':
+ app.run(host='0.0.0.0', port=5000, debug=True)
diff --git a/big-bear-casaos-user-management/app/templates/base.html b/big-bear-casaos-user-management/app/templates/base.html
new file mode 100644
index 0000000..5733ef6
--- /dev/null
+++ b/big-bear-casaos-user-management/app/templates/base.html
@@ -0,0 +1,15 @@
+
+
+
+ BigBearCasaOS User Management
+
+
+
+
+
+ {% block content %}{% endblock %}
+
+
diff --git a/big-bear-casaos-user-management/app/templates/index.html b/big-bear-casaos-user-management/app/templates/index.html
new file mode 100644
index 0000000..1beb4bc
--- /dev/null
+++ b/big-bear-casaos-user-management/app/templates/index.html
@@ -0,0 +1,128 @@
+{% extends "base.html" %} {% block content %}
+BigBearCasaOS User Management
+
+
+
+
+
+
+
+
+ ID |
+ Username |
+ Role |
+ Actions |
+
+
+
+ {% for user in users %}
+
+ {{ user[0] }} |
+ {{ user[1] }} |
+ {{ user[2] }} |
+
+
+
+ |
+
+ {% endfor %}
+
+
+
+
+ {% for user in users %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/big-bear-casaos-user-management/app/user_management.py b/big-bear-casaos-user-management/app/user_management.py
new file mode 100644
index 0000000..f192a02
--- /dev/null
+++ b/big-bear-casaos-user-management/app/user_management.py
@@ -0,0 +1,170 @@
+# Based on https://github.com/carlbomsdata/casaos-user-management
+# Original discussion: https://community.bigbeartechworld.com/t/casaos-user-management/2225
+# Modified version by bigbeartechworld: Added UI implementation and adapted user_management.py
+# to integrate with the UI while maintaining core functionality from the original.
+
+import sqlite3
+import os
+import shutil
+import subprocess
+import hashlib
+import getpass
+from datetime import datetime
+
+# Constants
+DB_PATH = "/var/lib/casaos/db/user.db"
+BACKUP_PATH = "/var/lib/casaos/db/user_backup.db"
+SERVICE_NAME = "casaos-user-service.service"
+
+# Helper Functions
+def check_casaos_installation():
+ if not os.path.exists(DB_PATH):
+ return False
+ service_status = subprocess.run(
+ ["systemctl", "status", SERVICE_NAME],
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+ return service_status.returncode == 0
+
+def backup_database():
+ try:
+ shutil.copy(DB_PATH, BACKUP_PATH)
+ return True
+ except Exception:
+ return False
+
+def hash_password(password):
+ """Hash the password using MD5."""
+ return hashlib.md5(password.encode()).hexdigest()
+
+def connect_db():
+ """Connect to the SQLite database."""
+ try:
+ return sqlite3.connect(DB_PATH)
+ except sqlite3.Error as e:
+ print(f"Database connection error: {e}")
+ exit(1)
+
+def manage_service(action):
+ """Start, stop, or restart the CasaOS user service on host OS."""
+ try:
+ result = subprocess.run(
+ ["systemctl", "--system", action, SERVICE_NAME],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ text=True,
+ check=True
+ )
+ return True
+ except subprocess.CalledProcessError as e:
+ print(f"Service {action} failed: {e.stderr}")
+ return False
+
+def list_users(return_data=False):
+ """List all users in the database."""
+ conn = connect_db()
+ cursor = conn.cursor()
+ try:
+ cursor.execute("SELECT id, username, role FROM o_users")
+ users = cursor.fetchall()
+ if return_data:
+ return users
+ else:
+ if users:
+ print("\nUsers in the database:")
+ for user in users:
+ print(f"ID: {user[0]}, Username: {user[1]}, Role: {user[2]}")
+ else:
+ print("\nNo users found.")
+ except sqlite3.Error as e:
+ print(f"Error fetching users: {e}")
+ return [] if return_data else None
+ finally:
+ conn.close()
+
+def add_user(username, password):
+ """Add a new user to the database."""
+ if not username or not password:
+ print("Username and password cannot be empty.")
+ return False
+
+ conn = connect_db()
+ cursor = conn.cursor()
+ try:
+ # Check for duplicate username
+ cursor.execute("SELECT 1 FROM o_users WHERE username = ?", (username,))
+ if cursor.fetchone():
+ print(f"Error: A user with the username '{username}' already exists.")
+ return False
+
+ hashed_password = hash_password(password)
+ created_at = datetime.now().isoformat()
+
+ # Insert the user into the database
+ cursor.execute(
+ "INSERT INTO o_users (username, password, role, created_at) VALUES (?, ?, ?, ?)",
+ (username, hashed_password, "admin", created_at),
+ )
+ conn.commit()
+ print(f"User '{username}' added successfully.")
+ return True
+ except sqlite3.Error as e:
+ print(f"Error adding user: {e}")
+ return False
+ finally:
+ conn.close()
+
+def edit_password(user_id, new_password):
+ if not user_id or not new_password:
+ return False
+
+ hashed_password = hash_password(new_password)
+ updated_at = datetime.now().isoformat()
+
+ conn = connect_db()
+ cursor = conn.cursor()
+ try:
+ cursor.execute(
+ "UPDATE o_users SET password = ?, updated_at = ? WHERE id = ?",
+ (hashed_password, updated_at, user_id),
+ )
+ if cursor.rowcount > 0:
+ conn.commit()
+ return True
+ return False
+ except sqlite3.Error:
+ return False
+ finally:
+ conn.close()
+
+def remove_user(user_id):
+ if not user_id:
+ return False
+
+ conn = connect_db()
+ cursor = conn.cursor()
+ try:
+ cursor.execute("DELETE FROM o_users WHERE id = ?", (user_id,))
+ if cursor.rowcount > 0:
+ conn.commit()
+ return True
+ return False
+ except sqlite3.Error:
+ return False
+ finally:
+ conn.close()
+
+def reset_database():
+ """Reset the database by deleting the user.db file."""
+ try:
+ if os.path.exists(DB_PATH):
+ os.remove(DB_PATH)
+ if not manage_service("restart"):
+ print("Warning: Database reset successful but service restart failed")
+ return True
+ return False
+ except Exception as e:
+ print(f"Reset failed: {str(e)}")
+ return False
+
diff --git a/big-bear-casaos-user-management/docker-compose.yml b/big-bear-casaos-user-management/docker-compose.yml
new file mode 100644
index 0000000..03153cb
--- /dev/null
+++ b/big-bear-casaos-user-management/docker-compose.yml
@@ -0,0 +1,45 @@
+services:
+ big-bear-casaos-user-management:
+ # The service name for the user management system
+ image: bigbeartechworld/big-bear-casaos-user-management:0.0.1
+ # The Docker image to use for this service, with the specified version tag
+ container_name: big-bear-casaos-user-management
+ # A custom name for the container, making it easier to identify
+
+ ports:
+ # Exposing port 5000 on the host and mapping it to port 5000 in the container
+ - "5000:5000"
+
+ volumes:
+ # Mounting the cgroup filesystem in read-only mode for system resource monitoring
+ - /sys/fs/cgroup:/sys/fs/cgroup:ro
+ # Persisting CasaOS database files to retain data across container restarts
+ - /var/lib/casaos/db:/var/lib/casaos/db
+ # Mounting systemd's runtime directory for integration with the host system
+ - /run/systemd/system:/run/systemd/system
+ # Providing access to the D-Bus system bus for communication with host services
+ - /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
+
+ # Ensures the container restarts automatically unless explicitly stopped
+ restart: unless-stopped
+
+ environment:
+ # Sets the Flask application entry point
+ - FLASK_APP=app.py
+ # Configures Flask to run in production mode
+ - FLASK_ENV=production
+ # Specifies the admin username for the service
+ - ADMIN_USERNAME=casaos
+ # Specifies the admin password for the service
+ - ADMIN_PASSWORD=casaos
+
+ # Grants the container extended privileges, necessary for some operations
+ privileged: true
+
+ cap_add:
+ # Adds the SYS_ADMIN capability to the container for advanced administrative actions
+ - SYS_ADMIN
+
+ security_opt:
+ # Disables the default seccomp security profile to allow unrestricted system calls
+ - seccomp:unconfined
\ No newline at end of file