package api import ( "fmt" "net/http" "sort" "strings" "tera-clone/services/tera-api/internal/domain" ) func (r *Router) authenticateActor(w http.ResponseWriter, req *http.Request) (domain.Actor, bool) { if token := bearerToken(req.Header.Get("Authorization")); token != "" { actor, err := r.store.ActorBySession(req.Context(), token) if err != nil { writeError(w, http.StatusUnauthorized, "authentication_required", "invalid session token") return domain.Actor{}, false } return actor, true } if r.disableMockAuth { writeError(w, http.StatusUnauthorized, "authentication_required", "authentication required") return domain.Actor{}, false } actorName := strings.TrimSpace(req.Header.Get("X-Mock-Actor")) if actorName == "" { writeError(w, http.StatusUnauthorized, "authentication_required", "authentication required") return domain.Actor{}, false } actor, ok := mockActor(actorName) if !ok { writeError(w, http.StatusUnauthorized, "authentication_required", "unknown actor") return domain.Actor{}, false } return actor, true } func bearerToken(header string) string { const prefix = "Bearer " if !strings.HasPrefix(header, prefix) { return "" } return strings.TrimSpace(strings.TrimPrefix(header, prefix)) } func authProfile(actor domain.Actor) domain.AuthProfile { return domain.AuthProfile{ Actor: actor.Name, Role: actor.Role, Permissions: sortedPermissions(actor.Permissions), Prototype: true, PortPermissions: actorPortPermissions(actor), MustChangePassword: actor.MustChangePassword, } } func actorPortPermissions(actor domain.Actor) []domain.PortPermission { result := make([]domain.PortPermission, 0, 48) defaultAccess := "read" if actor.Role == "administrator" || actor.Role == "port_operator" { defaultAccess = "write" } for index := 1; index <= 48; index++ { channel := fmt.Sprintf("CH%02d", index) access := actor.PortAccess[channel] if access == "" { access = defaultAccess } result = append(result, domain.PortPermission{Channel: channel, Access: access}) } return result } func sortedPermissions(permissions map[string]bool) []string { result := make([]string, 0, len(permissions)) for permission, allowed := range permissions { if allowed { result = append(result, permission) } } sort.Strings(result) return result } func mockActor(name string) (domain.Actor, bool) { switch name { case "mock-admin": return domain.Actor{ Name: name, Role: "administrator", Permissions: map[string]bool{ "alarm.read": true, "alarm.write": true, "audit.read": true, "auth.read": true, "auth.write": true, "config.read": true, "config.write": true, "diagnostics.run": true, "direct.view": true, "direct.write": true, "hardware.read": true, "hardware.write": true, "firmware.apply": true, "firmware.read": true, "firmware.upload": true, "log.export": true, "log.clear": true, "log.read": true, "network.read": true, "network.write": true, "operation.read": true, "port.read": true, "port.restart": true, "port.write": true, "system.factory_reset": true, "system.reboot": true, "system.read": true, "time.sync": true, }, }, true case "mock-port-operator": return domain.Actor{ Name: name, Role: "port_operator", Permissions: map[string]bool{ "direct.view": true, "direct.write": true, "port.read": true, "port.restart": true, "port.write": true, }, }, true case "mock-viewer": return domain.Actor{ Name: name, Role: "viewer", Permissions: map[string]bool{ "direct.view": true, }, }, true default: return domain.Actor{}, false } }