You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

149 lines
3.9 KiB

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
}
}