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
149 lines
3.9 KiB
|
2 months ago
|
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
|
||
|
|
}
|
||
|
|
}
|