mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-06 22:30:56 +03:00
EventManager: support placeholders within URL paths
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
2
go.mod
2
go.mod
@@ -37,7 +37,7 @@ require (
|
|||||||
github.com/hashicorp/go-retryablehttp v0.7.2
|
github.com/hashicorp/go-retryablehttp v0.7.2
|
||||||
github.com/jackc/pgx/v5 v5.2.0
|
github.com/jackc/pgx/v5 v5.2.0
|
||||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||||
github.com/klauspost/compress v1.15.14
|
github.com/klauspost/compress v1.15.15
|
||||||
github.com/lestrrat-go/jwx/v2 v2.0.8
|
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||||
github.com/mattn/go-sqlite3 v1.14.16
|
github.com/mattn/go-sqlite3 v1.14.16
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -1397,8 +1397,8 @@ github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
|
|||||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.14 h1:i7WCKDToww0wA+9qrUZ1xOjp218vfFo3nTU6UHp+gOc=
|
github.com/klauspost/compress v1.15.15 h1:EF27CXIuDsYJ6mmvtBRlEuB2UVOqHG1tAXgZ7yIO+lw=
|
||||||
github.com/klauspost/compress v1.15.14/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
github.com/klauspost/compress v1.15.15/go.mod h1:ZcK2JAFqKOpnBlxcLsJzYfrS9X1akm9fHZNnD9+Vo/4=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||||
|
|||||||
@@ -1029,11 +1029,22 @@ func checkEventGroupConditionPatters(groups []sdk.GroupMapping, patterns []datap
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPRuleActionEndpoint(c dataprovider.EventActionHTTPConfig, replacer *strings.Replacer) (string, error) {
|
func getHTTPRuleActionEndpoint(c dataprovider.EventActionHTTPConfig, replacer *strings.Replacer) (string, error) {
|
||||||
if len(c.QueryParameters) > 0 {
|
u, err := url.Parse(c.Endpoint)
|
||||||
u, err := url.Parse(c.Endpoint)
|
if err != nil {
|
||||||
if err != nil {
|
return "", fmt.Errorf("invalid endpoint: %w", err)
|
||||||
return "", fmt.Errorf("invalid endpoint: %w", err)
|
}
|
||||||
|
if strings.Contains(u.Path, "{{") {
|
||||||
|
pathComponents := strings.Split(u.Path, "/")
|
||||||
|
for idx := range pathComponents {
|
||||||
|
part := replaceWithReplacer(pathComponents[idx], replacer)
|
||||||
|
if part != pathComponents[idx] {
|
||||||
|
pathComponents[idx] = url.PathEscape(part)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
u.Path = ""
|
||||||
|
u = u.JoinPath(pathComponents...)
|
||||||
|
}
|
||||||
|
if len(c.QueryParameters) > 0 {
|
||||||
q := u.Query()
|
q := u.Query()
|
||||||
|
|
||||||
for _, keyVal := range c.QueryParameters {
|
for _, keyVal := range c.QueryParameters {
|
||||||
@@ -1041,9 +1052,8 @@ func getHTTPRuleActionEndpoint(c dataprovider.EventActionHTTPConfig, replacer *s
|
|||||||
}
|
}
|
||||||
|
|
||||||
u.RawQuery = q.Encode()
|
u.RawQuery = q.Encode()
|
||||||
return u.String(), nil
|
|
||||||
}
|
}
|
||||||
return c.Endpoint, nil
|
return u.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.MIMEHeader,
|
func writeHTTPPart(m *multipart.Writer, part dataprovider.HTTPPart, h textproto.MIMEHeader,
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -703,6 +704,7 @@ func TestEventRuleActions(t *testing.T) {
|
|||||||
if assert.Error(t, err) {
|
if assert.Error(t, err) {
|
||||||
assert.Contains(t, err.Error(), "unable to decrypt HTTP password")
|
assert.Contains(t, err.Error(), "unable to decrypt HTTP password")
|
||||||
}
|
}
|
||||||
|
action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
|
||||||
action.Options.HTTPConfig.Password = kms.NewEmptySecret()
|
action.Options.HTTPConfig.Password = kms.NewEmptySecret()
|
||||||
action.Options.HTTPConfig.Body = ""
|
action.Options.HTTPConfig.Body = ""
|
||||||
action.Options.HTTPConfig.Parts = []dataprovider.HTTPPart{
|
action.Options.HTTPConfig.Parts = []dataprovider.HTTPPart{
|
||||||
@@ -1890,3 +1892,33 @@ func getErrorString(err error) string {
|
|||||||
}
|
}
|
||||||
return err.Error()
|
return err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestHTTPEndpointWithPlaceholders(t *testing.T) {
|
||||||
|
c := dataprovider.EventActionHTTPConfig{
|
||||||
|
Endpoint: "http://127.0.0.1:8080/base/url/{{Name}}/{{VirtualPath}}/upload",
|
||||||
|
QueryParameters: []dataprovider.KeyValue{
|
||||||
|
{
|
||||||
|
Key: "u",
|
||||||
|
Value: "{{Name}}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: "p",
|
||||||
|
Value: "{{VirtualPath}}",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
name := "uname"
|
||||||
|
vPath := "/a dir/@ file.txt"
|
||||||
|
replacer := strings.NewReplacer("{{Name}}", name, "{{VirtualPath}}", vPath)
|
||||||
|
u, err := getHTTPRuleActionEndpoint(c, replacer)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expected := "http://127.0.0.1:8080/base/url/" + url.PathEscape(name) + "/" + url.PathEscape(vPath) +
|
||||||
|
"/upload?" + "p=" + url.QueryEscape(vPath) + "&u=" + url.QueryEscape(name)
|
||||||
|
assert.Equal(t, expected, u)
|
||||||
|
|
||||||
|
c.Endpoint = "http://127.0.0.1/upload"
|
||||||
|
u, err = getHTTPRuleActionEndpoint(c, replacer)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
expected = c.Endpoint + "?p=" + url.QueryEscape(vPath) + "&u=" + url.QueryEscape(name)
|
||||||
|
assert.Equal(t, expected, u)
|
||||||
|
}
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||||||
<input type="text" class="form-control" id="idHTTPEndpoint" name="http_endpoint" placeholder=""
|
<input type="text" class="form-control" id="idHTTPEndpoint" name="http_endpoint" placeholder=""
|
||||||
aria-describedby="HTTPEndpointHelpBlock" value="{{.Action.Options.HTTPConfig.Endpoint}}">
|
aria-describedby="HTTPEndpointHelpBlock" value="{{.Action.Options.HTTPConfig.Endpoint}}">
|
||||||
<small id="HTTPEndpointHelpBlock" class="form-text text-muted">
|
<small id="HTTPEndpointHelpBlock" class="form-text text-muted">
|
||||||
Endpoint URL, i.e https://host:port/path
|
Endpoint URL, i.e https://host:port/path. Placeholders are supported within the URL path
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user