|
@@ -11,28 +11,45 @@ import (
|
|
|
"syscall"
|
|
|
|
|
|
"github.com/gorilla/csrf"
|
|
|
+ "imuslab.com/dezukvm/dezukvmd/mod/auth"
|
|
|
"imuslab.com/dezukvm/dezukvmd/mod/dezukvm"
|
|
|
+ "imuslab.com/dezukvm/dezukvmd/mod/logger"
|
|
|
"imuslab.com/dezukvm/dezukvmd/mod/utils"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
dezukvmManager *dezukvm.DezukVM
|
|
|
- csrfMiddleware func(http.Handler) http.Handler
|
|
|
listeningServerMux *http.ServeMux
|
|
|
+ authManager *auth.AuthManager
|
|
|
+ systemLogger *logger.Logger
|
|
|
)
|
|
|
|
|
|
-func init() {
|
|
|
- csrfMiddleware = csrf.Protect(
|
|
|
- []byte(nodeUUID),
|
|
|
- csrf.CookieName("dezukvm_csrf_token"),
|
|
|
- csrf.Secure(false),
|
|
|
- csrf.Path("/"),
|
|
|
- csrf.SameSite(csrf.SameSiteLaxMode),
|
|
|
- )
|
|
|
+func init_auth_manager() error {
|
|
|
+ // Initialize logger
|
|
|
+ systemLogger = logger.NewLogger(logger.WithLogLevel(logger.InfoLevel))
|
|
|
+
|
|
|
+ // Initialize AuthManager with logger and DB path
|
|
|
+ var err error
|
|
|
+ authManager, err = auth.NewAuthManager(auth.Options{
|
|
|
+ DBPath: DB_FILE_PATH,
|
|
|
+ Log: systemLogger.Info,
|
|
|
+ })
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return nil
|
|
|
}
|
|
|
|
|
|
func init_ipkvm_mode() error {
|
|
|
listeningServerMux = http.NewServeMux()
|
|
|
+
|
|
|
+ // Initialize the Auth Manager
|
|
|
+ err := init_auth_manager()
|
|
|
+ if err != nil {
|
|
|
+ log.Fatal("Failed to initialize Auth Manager:", err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
//Create a new DezukVM manager
|
|
|
dezukvmManager = dezukvm.NewKvmHostInstance(&dezukvm.RuntimeOptions{
|
|
|
EnableLog: true,
|
|
@@ -57,6 +74,9 @@ func init_ipkvm_mode() error {
|
|
|
}
|
|
|
// ~Experimental
|
|
|
|
|
|
+ // Handle root routing with CSRF protection
|
|
|
+ handle_root_routing(listeningServerMux)
|
|
|
+
|
|
|
// Handle program exit to close the HID controller
|
|
|
c := make(chan os.Signal, 1)
|
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
@@ -67,12 +87,51 @@ func init_ipkvm_mode() error {
|
|
|
if dezukvmManager != nil {
|
|
|
dezukvmManager.Close()
|
|
|
}
|
|
|
+ if authManager != nil {
|
|
|
+ authManager.Close()
|
|
|
+ }
|
|
|
log.Println("Shutdown complete.")
|
|
|
os.Exit(0)
|
|
|
}()
|
|
|
|
|
|
- // Middleware to inject CSRF token into HTML files served from www
|
|
|
- listeningServerMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ // Register Auth related APIs
|
|
|
+ register_auth_apis(listeningServerMux)
|
|
|
+
|
|
|
+ // Register DezukVM related APIs
|
|
|
+ register_ipkvm_apis(listeningServerMux)
|
|
|
+
|
|
|
+ err = http.ListenAndServe(":9000", listeningServerMux)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func request_url_allow_unauthenticated(r *http.Request) bool {
|
|
|
+ // Define a list of URL paths that can be accessed without authentication
|
|
|
+ allowedPaths := []string{
|
|
|
+ "/login.html",
|
|
|
+ "/api/v1/login",
|
|
|
+ "/img/",
|
|
|
+ "/js/",
|
|
|
+ "/css/",
|
|
|
+ "/favicon.png",
|
|
|
+ }
|
|
|
+
|
|
|
+ requestPath := r.URL.Path
|
|
|
+ for _, path := range allowedPaths {
|
|
|
+ if strings.HasPrefix(requestPath, path) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func handle_root_routing(mux *http.ServeMux) {
|
|
|
+ // Root router: check login status, redirect to login.html if not authenticated
|
|
|
+ mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if !authManager.UserIsLoggedIn(r) && !request_url_allow_unauthenticated(r) {
|
|
|
+ w.Header().Set("Location", "/login.html")
|
|
|
+ w.WriteHeader(http.StatusFound)
|
|
|
+ return
|
|
|
+ }
|
|
|
// Only inject for .html files
|
|
|
path := r.URL.Path
|
|
|
if path == "/" {
|
|
@@ -93,35 +152,62 @@ func init_ipkvm_mode() error {
|
|
|
w.Write([]byte(htmlContent))
|
|
|
return
|
|
|
}
|
|
|
- // Fallback to static file server for non-HTML files
|
|
|
- http.FileServer(http.Dir("www")).ServeHTTP(w, r)
|
|
|
- })
|
|
|
|
|
|
- // Register DezukVM related APIs
|
|
|
- register_ipkvm_apis(listeningServerMux)
|
|
|
+ // Serve static files (img, js, css, etc.)
|
|
|
+ targetFilePath := filepath.Join("www", filepath.Clean(path))
|
|
|
+ http.ServeFile(w, r, targetFilePath)
|
|
|
+ })
|
|
|
+}
|
|
|
|
|
|
- err = http.ListenAndServe(":9000", listeningServerMux)
|
|
|
- return err
|
|
|
+func register_auth_apis(mux *http.ServeMux) {
|
|
|
+ // Check API for session validation
|
|
|
+ mux.HandleFunc("/api/v1/check", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if r.Method != http.MethodPost {
|
|
|
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ ok := authManager.UserIsLoggedIn(r)
|
|
|
+ if !ok {
|
|
|
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ w.WriteHeader(http.StatusOK)
|
|
|
+ w.Write([]byte("{\"status\":\"ok\"}"))
|
|
|
+ })
|
|
|
+ // Login API
|
|
|
+ mux.HandleFunc("/api/v1/login", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if r.Method != http.MethodPost {
|
|
|
+ http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ err := authManager.LoginUser(w, r)
|
|
|
+ if err != nil {
|
|
|
+ http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ w.WriteHeader(http.StatusOK)
|
|
|
+ w.Write([]byte("{\"status\":\"success\"}"))
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
func register_ipkvm_apis(mux *http.ServeMux) {
|
|
|
- mux.HandleFunc("/api/v1/stream/{uuid}/video", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ authManager.HandleFunc("/api/v1/stream/{uuid}/video", func(w http.ResponseWriter, r *http.Request) {
|
|
|
instanceUUID := r.PathValue("uuid")
|
|
|
fmt.Println("Requested video stream for instance UUID:", instanceUUID)
|
|
|
dezukvmManager.HandleVideoStreams(w, r, instanceUUID)
|
|
|
- })
|
|
|
+ }, mux)
|
|
|
|
|
|
- mux.HandleFunc("/api/v1/stream/{uuid}/audio", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ authManager.HandleFunc("/api/v1/stream/{uuid}/audio", func(w http.ResponseWriter, r *http.Request) {
|
|
|
instanceUUID := r.PathValue("uuid")
|
|
|
dezukvmManager.HandleAudioStreams(w, r, instanceUUID)
|
|
|
- })
|
|
|
+ }, mux)
|
|
|
|
|
|
- mux.HandleFunc("/api/v1/hid/{uuid}/events", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ authManager.HandleFunc("/api/v1/hid/{uuid}/events", func(w http.ResponseWriter, r *http.Request) {
|
|
|
instanceUUID := r.PathValue("uuid")
|
|
|
dezukvmManager.HandleHIDEvents(w, r, instanceUUID)
|
|
|
- })
|
|
|
+ }, mux)
|
|
|
|
|
|
- mux.HandleFunc("/api/v1/mass_storage/switch", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ authManager.HandleFunc("/api/v1/mass_storage/switch", func(w http.ResponseWriter, r *http.Request) {
|
|
|
if r.Method != http.MethodPost {
|
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
return
|
|
@@ -144,13 +230,13 @@ func register_ipkvm_apis(mux *http.ServeMux) {
|
|
|
default:
|
|
|
http.Error(w, "Invalid side parameter", http.StatusBadRequest)
|
|
|
}
|
|
|
- })
|
|
|
+ }, mux)
|
|
|
|
|
|
- mux.HandleFunc("/api/v1/instances", func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ authManager.HandleFunc("/api/v1/instances", func(w http.ResponseWriter, r *http.Request) {
|
|
|
if r.Method == http.MethodGet {
|
|
|
dezukvmManager.HandleListInstances(w, r)
|
|
|
} else {
|
|
|
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
|
|
}
|
|
|
- })
|
|
|
+ }, mux)
|
|
|
}
|