1+ package proxy
2+
3+ import (
4+ "bytes"
5+ "encoding/base64"
6+ "io"
7+ "net/http"
8+ "net/http/httputil"
9+ "net/url"
10+ "os"
11+ "slices"
12+ "strings"
13+ "text/template"
14+
15+ log "github.com/codeshelldev/secured-signal-api/utils"
16+ )
17+
18+ type AuthType string
19+
20+ const (
21+ Bearer AuthType = "Bearer"
22+ Basic AuthType = "Basic"
23+ Query AuthType = "Query"
24+ None AuthType = ""
25+ )
26+
27+ func getAuthType (str string ) AuthType {
28+ switch str {
29+ case "Bearer" :
30+ return Bearer
31+ case "Basic" :
32+ return Basic
33+ default :
34+ return None
35+ }
36+ }
37+
38+ func renderTemplate (name string , tmplStr string , data any ) (string , error ) {
39+ tmpl , err := template .New (name ).Parse (tmplStr )
40+
41+ if err != nil {
42+ return "" , err
43+ }
44+ var buf bytes.Buffer
45+
46+ err = tmpl .Execute (& buf , data )
47+
48+ if err != nil {
49+ return "" , err
50+ }
51+ return buf .String (), nil
52+ }
53+
54+ func AuthMiddleware (next http.Handler ) http.Handler {
55+ return http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
56+ log .Info ("Request:" , req .Method , req .URL .Path )
57+
58+ token := os .Getenv ("API_TOKEN" )
59+ user := "api"
60+
61+ authHeader := req .Header .Get ("Authorization" )
62+
63+ authQuery := req .URL .Query ().Get ("authorization" )
64+
65+ var authType AuthType
66+
67+ success := false
68+
69+ if authHeader != "" {
70+ authBody := strings .Split (authHeader , " " )
71+
72+ authType = getAuthType (authBody [0 ])
73+ authToken := authBody [1 ]
74+
75+ switch authType {
76+ case Bearer :
77+ if authToken == token {
78+ success = true
79+ }
80+
81+ case Basic :
82+ basicAuthBody , err := base64 .StdEncoding .DecodeString (authToken )
83+
84+ if err != nil {
85+ log .Error ("Could not decode Basic Auth Payload: " , err .Error ())
86+ }
87+
88+ basicAuth := string (basicAuthBody )
89+ basicAuthParams := strings .Split (basicAuth , ":" )
90+
91+ if basicAuthParams [0 ] == user && basicAuthParams [1 ] == token {
92+ success = true
93+ }
94+ }
95+
96+ } else if authQuery != "" {
97+ authType = Query
98+
99+ authToken , _ := url .QueryUnescape (authQuery )
100+
101+ if authToken == token {
102+ success = true
103+ }
104+ }
105+
106+ if ! success {
107+ log .Warn ("User failed " , string (authType ), " Auth" )
108+ http .Error (w , "Unauthorized" , http .StatusUnauthorized )
109+ return
110+ }
111+
112+ next .ServeHTTP (w , req )
113+ })
114+ }
115+
116+ func BlockedEndpointMiddleware (next http.Handler , BLOCKED_ENDPOINTS []string ) http.Handler {
117+ return http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
118+ reqPath := req .URL .Path
119+
120+ if slices .Contains (BLOCKED_ENDPOINTS , reqPath ) {
121+ log .Warn ("User tried to access blocked endpoint: " , reqPath )
122+ http .Error (w , "Unauthorized" , http .StatusUnauthorized )
123+ return
124+ }
125+
126+ next .ServeHTTP (w , req )
127+ })
128+ }
129+
130+ func TemplatingMiddleware (next http.Handler , VARIABLES map [string ]string ) http.Handler {
131+ return http .HandlerFunc (func (w http.ResponseWriter , req * http.Request ) {
132+ if req .Body != nil {
133+ bodyBytes , err := io .ReadAll (req .Body )
134+
135+ if err != nil {
136+ log .Error ("Could not decode Body: " , err .Error ())
137+ http .Error (w , "Internal Error" , http .StatusInternalServerError )
138+ return
139+ }
140+
141+ req .Body .Close ()
142+
143+ modifiedBody := string (bodyBytes )
144+
145+ modifiedBody , _ = renderTemplate ("json" , modifiedBody , VARIABLES )
146+ modifiedBodyBytes := []byte (modifiedBody )
147+
148+ req .Body = io .NopCloser (bytes .NewReader (modifiedBodyBytes ))
149+ }
150+
151+ reqPath := req .URL .Path
152+
153+ modifiedReqPath , _ := renderTemplate ("path" , reqPath , VARIABLES )
154+
155+ req .URL .Path = modifiedReqPath
156+
157+ next .ServeHTTP (w , req )
158+ })
159+ }
160+
161+ func Create (targetUrl string ) * httputil.ReverseProxy {
162+ url , _ := url .Parse (targetUrl )
163+
164+ proxy := httputil .NewSingleHostReverseProxy (url )
165+
166+ return proxy
167+ }
0 commit comments