...
1 package authserver
2
3 import (
4 "fmt"
5 "net/http"
6 "path"
7 "strings"
8
9 "github.com/gin-contrib/sessions"
10 "github.com/gin-gonic/gin"
11
12 authproxy "edge-infra.dev/pkg/edge/auth-proxy/types"
13 )
14
15
16
17
18
19 type httpError struct {
20 statusCode int
21 err error
22 }
23
24 func (h *httpError) StatusCode() int {
25 return h.statusCode
26 }
27
28 func (h *httpError) Error() string {
29 return h.err.Error()
30 }
31
32 func (h *httpError) Unwrap() error {
33 return h.err
34 }
35
36 type checkFunc func(*AuthServer, *gin.Context, sessions.Session) error
37
38
39
40
41 type check struct {
42
43
44 pathFilter string
45 checkFunc checkFunc
46 }
47
48
49 var allChecks = []check{
50 {
51 pathFilter: "",
52 checkFunc: (*AuthServer).rejectDuplicateSlashes,
53 },
54 {
55 pathFilter: "",
56 checkFunc: (*AuthServer).sessionUsernameCheck,
57 },
58 {
59 pathFilter: "/novnc/",
60 checkFunc: (*AuthServer).validateVNCRoles,
61 },
62 {
63
64 pathFilter: "/novnc/authorize",
65 checkFunc: (*AuthServer).injectVNCAuthHeaders,
66 },
67 {
68 pathFilter: "/novnc/write/authorize",
69 checkFunc: (*AuthServer).injectVNCAuthHeaders,
70 },
71 {
72 pathFilter: "/novnc/read/authorize",
73 checkFunc: (*AuthServer).injectVNCAuthHeaders,
74 },
75 }
76
77
78
79 func (as *AuthServer) authFilter(ctx *gin.Context, session sessions.Session) error {
80 for _, check := range as.checks {
81 if !strings.Contains(ctx.Request.URL.Path, check.pathFilter) {
82 continue
83 }
84
85 err := check.checkFunc(as, ctx, session)
86 if err != nil {
87 return err
88 }
89 }
90
91 return nil
92 }
93
94
95
96
97 func setBoolHeader(ctx *gin.Context, header string, value bool) {
98 if value {
99 ctx.Header(header, "1")
100 } else {
101 ctx.Header(header, "")
102 }
103 }
104
105
106
107
108 func getClusterEdgeIDFromPath(path string) string {
109 parts := strings.Split(path, "/")
110
111
112 if len(parts) < 4 || parts[1] != "remoteaccess" {
113 return ""
114 }
115 return parts[2]
116 }
117
118
119
120
121
122
123
124
125 func (as *AuthServer) rejectDuplicateSlashes(ctx *gin.Context, _ sessions.Session) error {
126 if ctx.Request.URL.Path == "" || ctx.Request.URL.Path == "/" {
127
128 return nil
129 }
130
131 originalPath := ctx.Request.URL.EscapedPath()
132
133
134
135 originalPath, _ = strings.CutSuffix(originalPath, "/")
136
137
138
139
140
141 originalPath = strings.Replace(originalPath, "/novnc//ws", "/novnc/ws", 1)
142
143 cleanedPath := ctx.Request.URL.EscapedPath()
144 cleanedPath = path.Clean(cleanedPath)
145
146 if cleanedPath != originalPath {
147 return &httpError{
148 statusCode: http.StatusBadRequest,
149 err: fmt.Errorf("invalid path"),
150 }
151 }
152 return nil
153 }
154
155 func (as *AuthServer) sessionUsernameCheck(ctx *gin.Context, session sessions.Session) error {
156 usr, ok := session.Get(authproxy.SessionUsernameField).(string)
157 if !ok {
158 return &httpError{
159 statusCode: http.StatusUnauthorized,
160 err: fmt.Errorf("Could not find session username"),
161 }
162 }
163 ctx.Header(headerKeyWebauthUser, usr)
164 return nil
165 }
166
View as plain text