Skip to content

Commit c787372

Browse files
authored
Merge pull request #29 from CodeShellDev/dev
Update
2 parents 4a2bf61 + 1e1d14b commit c787372

File tree

11 files changed

+443
-143
lines changed

11 files changed

+443
-143
lines changed

.github/templates/README.template.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,15 @@ Because Secured Signal API is just a Proxy you can use all of the [Signal REST A
177177
| **/v1/accounts** |
178178
| **/v1/contacts** |
179179
180-
These Endpoints are blocked by default due to Security Risks, but can be modified by setting `BLOCKED_ENDPOINTS` to a valid json array string
180+
These Endpoints are blocked by default due to Security Risks, but can be modified by setting `BLOCKED_ENDPOINTS` to a Comma seperated List:
181181

182182
```yaml
183183
environment:
184-
BLOCKED_ENDPOINTS: '[ "/v1/register","/v1/unregister","/v1/qrcodelink","/v1/contacts" ]'
184+
BLOCKED_ENDPOINTS: |
185+
/v1/register,
186+
/v1/unregister,
187+
/v1/qrcodelink,
188+
/v1/contacts,
185189
```
186190

187191
#### Variables
@@ -206,7 +210,8 @@ Set this Environment Variable to automatically provide default Recipients:
206210

207211
```yaml
208212
environment:
209-
RECIPIENTS: ' [ "user.id", "000", "001", "group.id" ] '
213+
RECIPIENTS: |
214+
user.id, 000, 001, group.id,
210215
```
211216

212217
example:

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module github.com/codeshelldev/secured-signal-api
22

3-
go 1.24.5
3+
go 1.25.1
44

55
require go.uber.org/zap v1.27.0
66

internals/proxy/middlewares/body.go

Lines changed: 41 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ package middlewares
22

33
import (
44
"bytes"
5-
"encoding/json"
65
"io"
76
"net/http"
87
"strconv"
98

109
log "github.com/codeshelldev/secured-signal-api/utils/logger"
10+
request "github.com/codeshelldev/secured-signal-api/utils/request"
1111
)
1212

1313
type MessageAlias struct {
@@ -25,65 +25,66 @@ func (data BodyMiddleware) Use() http.Handler {
2525
messageAliases := data.MessageAliases
2626

2727
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
28-
bodyBytes, err := io.ReadAll(req.Body)
28+
body, err := request.GetReqBody(w, req)
29+
2930
if err != nil {
30-
log.Error("Could not read Body: ", err.Error())
31-
http.Error(w, "Bad Request", http.StatusBadRequest)
32-
return
31+
log.Error("Could not get Request Body: ", err.Error())
3332
}
34-
defer req.Body.Close()
3533

36-
if len(bodyBytes) > 0 {
34+
var modifiedBody bool
35+
var bodyData map[string]interface{}
36+
37+
if !body.Empty {
38+
bodyData = body.Data
3739

38-
req.Body.Close()
40+
content, ok := bodyData["message"]
3941

40-
var modifiedBodyData map[string]interface{}
42+
if !ok || content == "" {
4143

42-
err = json.Unmarshal(bodyBytes, &modifiedBodyData)
44+
bodyData["message"], bodyData = getMessage(messageAliases, bodyData)
45+
46+
modifiedBody = true
47+
}
48+
}
49+
50+
if modifiedBody {
51+
modifiedBody, err := request.CreateBody(bodyData)
4352

4453
if err != nil {
45-
log.Error("Could not decode Body: ", err.Error())
4654
http.Error(w, "Internal Error", http.StatusInternalServerError)
4755
return
4856
}
4957

50-
content, ok := modifiedBodyData["message"]
51-
52-
if !ok || content == "" {
53-
best := 0
54-
55-
for _, alias := range messageAliases {
56-
aliasKey := alias.Alias
57-
priority := alias.Priority
58+
body = modifiedBody
5859

59-
value, ok := modifiedBodyData[aliasKey]
60+
strData := body.ToString()
6061

61-
if ok && value != "" && priority > best {
62-
content = modifiedBodyData[aliasKey]
63-
}
62+
req.ContentLength = int64(len(strData))
63+
req.Header.Set("Content-Length", strconv.Itoa(len(strData)))
64+
}
6465

65-
modifiedBodyData[aliasKey] = nil
66-
}
66+
req.Body = io.NopCloser(bytes.NewReader(body.Raw))
6767

68-
modifiedBodyData["message"] = content
68+
next.ServeHTTP(w, req)
69+
})
70+
}
6971

70-
bodyBytes, err = json.Marshal(modifiedBodyData)
72+
func getMessage(aliases []MessageAlias, data map[string]interface{}) (string, map[string]interface{}) {
73+
var content string
74+
var best int
7175

72-
if err != nil {
73-
log.Error("Could not encode Body: ", err.Error())
74-
http.Error(w, "Internal Error", http.StatusInternalServerError)
75-
return
76-
}
76+
for _, alias := range aliases {
77+
aliasKey := alias.Alias
78+
priority := alias.Priority
7779

78-
modifiedBody := string(bodyBytes)
80+
value, ok := data[aliasKey]
7981

80-
req.ContentLength = int64(len(modifiedBody))
81-
req.Header.Set("Content-Length", strconv.Itoa(len(modifiedBody)))
82-
}
82+
if ok && value != "" && priority > best {
83+
content = data[aliasKey].(string)
8384
}
8485

85-
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
86+
data[aliasKey] = nil
87+
}
8688

87-
next.ServeHTTP(w, req)
88-
})
89-
}
89+
return content, data
90+
}

internals/proxy/middlewares/template.go

Lines changed: 109 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,75 @@ import (
1313

1414
log "github.com/codeshelldev/secured-signal-api/utils/logger"
1515
query "github.com/codeshelldev/secured-signal-api/utils/query"
16+
request "github.com/codeshelldev/secured-signal-api/utils/request"
1617
)
1718

1819
type TemplateMiddleware struct {
1920
Next http.Handler
2021
Variables map[string]interface{}
2122
}
2223

24+
func (data TemplateMiddleware) Use() http.Handler {
25+
next := data.Next
26+
VARIABLES := data.Variables
27+
28+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
29+
body, err := request.GetReqBody(w, req)
30+
31+
if err != nil {
32+
log.Error("Could not get Request Body: ", err.Error())
33+
}
34+
35+
bodyData := map[string]interface{}{}
36+
37+
var modifiedBody bool
38+
39+
if !body.Empty {
40+
var modified bool
41+
42+
bodyData, modified = templateJSON(body.Data, VARIABLES)
43+
44+
if modified {
45+
modifiedBody = true
46+
}
47+
}
48+
49+
if req.URL.RawQuery != "" {
50+
var modified bool
51+
52+
req.URL.RawQuery, bodyData, modified = templateQuery(bodyData, req.URL, VARIABLES)
53+
54+
if modified {
55+
modifiedBody = true
56+
}
57+
}
58+
59+
if modifiedBody {
60+
modifiedBody, err := request.CreateBody(bodyData)
61+
62+
if err != nil {
63+
http.Error(w, "Internal Error", http.StatusInternalServerError)
64+
return
65+
}
66+
67+
body = modifiedBody
68+
69+
strData := body.ToString()
70+
71+
log.Debug("Applied Body Templating: ", strData)
72+
73+
req.ContentLength = int64(len(strData))
74+
req.Header.Set("Content-Length", strconv.Itoa(len(strData)))
75+
}
76+
77+
req.Body = io.NopCloser(bytes.NewReader(body.Raw))
78+
79+
req.URL.Path, _ = templatePath(req.URL, VARIABLES)
80+
81+
next.ServeHTTP(w, req)
82+
})
83+
}
84+
2385
func renderTemplate(name string, tmplStr string, data any) (string, error) {
2486
tmpl, err := template.New(name).Parse(tmplStr)
2587

@@ -36,15 +98,17 @@ func renderTemplate(name string, tmplStr string, data any) (string, error) {
3698
return buf.String(), nil
3799
}
38100

39-
func templateJSON(data map[string]interface{}, variables map[string]interface{}) map[string]interface{} {
101+
func templateJSON(data map[string]interface{}, variables map[string]interface{}) (map[string]interface{}, bool) {
102+
var modified bool
103+
40104
for k, v := range data {
41105
str, ok := v.(string)
42106

43107
if ok {
44108
re, err := regexp.Compile(`{{\s*\.([A-Za-z_][A-Za-z0-9_]*)\s*}}`)
45109

46110
if err != nil {
47-
log.Error("Encountered Error while Compiling Regex: ", err.Error())
111+
log.Error("Error while Compiling Regex: ", err.Error())
48112
}
49113

50114
matches := re.FindAllStringSubmatch(str, -1)
@@ -63,95 +127,77 @@ func templateJSON(data map[string]interface{}, variables map[string]interface{})
63127

64128
data[k] = strings.ReplaceAll(str, string(variable), tmplStr[0])
65129
}
130+
131+
modified = true
66132
} else if len(matches) == 1 {
67133
tmplKey := matches[0][1]
68134

69135
data[k] = variables[tmplKey]
136+
137+
modified = true
70138
}
71139
}
72140
}
73141

74-
return data
142+
return data, modified
75143
}
76144

77-
func (data TemplateMiddleware) Use() http.Handler {
78-
next := data.Next
79-
VARIABLES := data.Variables
80-
81-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
82-
bodyBytes, err := io.ReadAll(req.Body)
83-
if err != nil {
84-
log.Error("Could not read Body: ", err.Error())
85-
http.Error(w, "Bad Request", http.StatusBadRequest)
86-
return
87-
}
88-
defer req.Body.Close()
145+
func templatePath(reqUrl *url.URL, VARIABLES interface{}) (string, bool) {
146+
var modified bool
89147

90-
if len(bodyBytes) > 0 {
148+
reqPath, err := url.PathUnescape(reqUrl.Path)
91149

92-
var modifiedBodyData map[string]interface{}
93-
94-
err = json.Unmarshal(bodyBytes, &modifiedBodyData)
95-
96-
if err != nil {
97-
log.Error("Could not decode Body: ", err.Error())
98-
http.Error(w, "Internal Error", http.StatusInternalServerError)
99-
return
100-
}
101-
102-
modifiedBodyData = templateJSON(modifiedBodyData, VARIABLES)
103-
104-
if req.URL.RawQuery != "" {
105-
decodedQuery, _ := url.QueryUnescape(req.URL.RawQuery)
150+
if err != nil {
151+
log.Error("Error while Escaping Path: ", err.Error())
152+
return reqUrl.Path, modified
153+
}
106154

107-
log.Debug("Decoded Query: ", decodedQuery)
155+
reqPath, err = renderTemplate("path", reqPath, VARIABLES)
108156

109-
templatedQuery, _ := renderTemplate("query", decodedQuery, VARIABLES)
157+
if err != nil {
158+
log.Error("Could not Template Path: ", err.Error())
159+
return reqUrl.Path, modified
160+
}
110161

111-
modifiedQuery := req.URL.Query()
162+
if reqUrl.Path != reqPath {
163+
log.Debug("Applied Path Templating: ", reqPath)
112164

113-
queryData := query.ParseRawQuery(templatedQuery)
165+
modified = true
166+
}
114167

115-
for key, value := range queryData {
116-
keyWithoutPrefix, found := strings.CutPrefix(key, "@")
168+
return reqPath, modified
169+
}
117170

118-
if found {
119-
modifiedBodyData[keyWithoutPrefix] = query.ParseTypedQuery(value)
171+
func templateQuery(data map[string]interface{}, reqUrl *url.URL, VARIABLES interface{}) (string, map[string]interface{}, bool) {
172+
var modified bool
120173

121-
modifiedQuery.Del(key)
122-
}
123-
}
174+
decodedQuery, _ := url.QueryUnescape(reqUrl.RawQuery)
124175

125-
req.URL.RawQuery = modifiedQuery.Encode()
176+
log.Debug("Decoded Query: ", decodedQuery)
126177

127-
log.Debug("Applied Query Templating: ", templatedQuery)
128-
}
178+
templatedQuery, _ := renderTemplate("query", decodedQuery, VARIABLES)
129179

130-
bodyBytes, err = json.Marshal(modifiedBodyData)
180+
modifiedQuery := reqUrl.Query()
131181

132-
if err != nil {
133-
log.Error("Could not encode Body: ", err.Error())
134-
http.Error(w, "Internal Error", http.StatusInternalServerError)
135-
return
136-
}
182+
queryData := query.ParseRawQuery(templatedQuery)
137183

138-
modifiedBody := string(bodyBytes)
184+
for key, value := range queryData {
185+
keyWithoutPrefix, found := strings.CutPrefix(key, "@")
139186

140-
log.Debug("Applied Body Templating: ", modifiedBody)
187+
if found {
188+
data[keyWithoutPrefix] = query.ParseTypedQuery(value)
141189

142-
req.ContentLength = int64(len(modifiedBody))
143-
req.Header.Set("Content-Length", strconv.Itoa(len(modifiedBody)))
190+
modifiedQuery.Del(key)
144191
}
192+
}
145193

146-
req.Body = io.NopCloser(bytes.NewReader(bodyBytes))
147-
148-
reqPath := req.URL.Path
149-
reqPath, _ = url.PathUnescape(reqPath)
194+
reqRawQuery := modifiedQuery.Encode()
150195

151-
modifiedReqPath, _ := renderTemplate("path", reqPath, VARIABLES)
196+
if reqUrl.Query().Encode() != reqRawQuery {
197+
log.Debug("Applied Query Templating: ", templatedQuery)
152198

153-
req.URL.Path = modifiedReqPath
199+
modified = true
200+
}
154201

155-
next.ServeHTTP(w, req)
156-
})
157-
}
202+
return reqRawQuery, data, modified
203+
}

0 commit comments

Comments
 (0)