Initial QSfera import
This commit is contained in:
+38
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// WriteErrorPage create a formatted error page response containing the provided
|
||||
// information and writes it to the provided http.ResponseWriter.
|
||||
func WriteErrorPage(rw http.ResponseWriter, code int, title string, message string) {
|
||||
if title == "" {
|
||||
title = http.StatusText(code)
|
||||
}
|
||||
|
||||
text := fmt.Sprintf("%d %s", code, title)
|
||||
if message != "" {
|
||||
text = text + " - " + message
|
||||
}
|
||||
|
||||
http.Error(rw, text, code)
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrorWithDescription is an interface binding the standard error
|
||||
// inteface with a description.
|
||||
type ErrorWithDescription interface {
|
||||
error
|
||||
Description() string
|
||||
}
|
||||
|
||||
// DescribeError returns a wrapped version for errors which contain additional
|
||||
// fields. The wrapped version contains all fields as a string value. Use this
|
||||
// for general purpose logging of rich errors.
|
||||
func DescribeError(err error) error {
|
||||
switch err.(type) {
|
||||
case ErrorWithDescription:
|
||||
err = fmt.Errorf("%s - %s", err, err.(ErrorWithDescription).Description())
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// ErrorAsFields returns a mapping of all fields of the provided error.
|
||||
func ErrorAsFields(err error) map[string]interface{} {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{})
|
||||
fields["error"] = err.Error()
|
||||
switch err.(type) {
|
||||
case ErrorWithDescription:
|
||||
fields["desc"] = err.(ErrorWithDescription).Description()
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/http2"
|
||||
|
||||
"github.com/libregraph/lico/version"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultHTTPTimeout = 30 * time.Second
|
||||
defaultHTTPKeepAlive = 30 * time.Second
|
||||
defaultHTTPMaxIdleConns = 100
|
||||
defaultHTTPIdleConnTimeout = 90 * time.Second
|
||||
defaultHTTPTLSHandshakeTimeout = 10 * time.Second
|
||||
defaultHTTPExpectContinueTimeout = 1 * time.Second
|
||||
)
|
||||
|
||||
// DefaultHTTPUserAgent is the User-Agent Header which should be used when
|
||||
// making HTTP requests.
|
||||
var DefaultHTTPUserAgent = "LibreGraph-Connect/" + version.Version
|
||||
|
||||
// HTTPTransportWithTLSClientConfig creates a new http.Transport with sane
|
||||
// default settings using the provided tls.Config.
|
||||
func HTTPTransportWithTLSClientConfig(tlsClientConfig *tls.Config) *http.Transport {
|
||||
transport := &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DialContext: (&net.Dialer{
|
||||
Timeout: defaultHTTPTimeout,
|
||||
KeepAlive: defaultHTTPKeepAlive,
|
||||
DualStack: true,
|
||||
}).DialContext,
|
||||
MaxIdleConns: defaultHTTPMaxIdleConns,
|
||||
IdleConnTimeout: defaultHTTPIdleConnTimeout,
|
||||
TLSHandshakeTimeout: defaultHTTPTLSHandshakeTimeout,
|
||||
ExpectContinueTimeout: defaultHTTPExpectContinueTimeout,
|
||||
}
|
||||
if tlsClientConfig != nil {
|
||||
transport.TLSClientConfig = tlsClientConfig
|
||||
err := http2.ConfigureTransport(transport)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
return transport
|
||||
}
|
||||
|
||||
// DefaultTLSConfig returns a new tls.Config.
|
||||
func DefaultTLSConfig() *tls.Config {
|
||||
return &tls.Config{
|
||||
ClientSessionCache: tls.NewLRUClientSessionCache(0),
|
||||
}
|
||||
}
|
||||
|
||||
// InsecureSkipVerifyTLSConfig returns a new tls.Config which does skip TLS verification.
|
||||
func InsecureSkipVerifyTLSConfig() *tls.Config {
|
||||
config := DefaultTLSConfig()
|
||||
config.InsecureSkipVerify = true
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// DefaultHTTPClient is a http.Client with a timeout set.
|
||||
var DefaultHTTPClient = &http.Client{
|
||||
Timeout: defaultHTTPTimeout,
|
||||
Transport: HTTPTransportWithTLSClientConfig(DefaultTLSConfig()),
|
||||
}
|
||||
|
||||
// InsecureHTTPClient is a http.Client with a timeout set and with TLS
|
||||
// verification disabled.
|
||||
var InsecureHTTPClient = &http.Client{
|
||||
Timeout: defaultHTTPTimeout,
|
||||
Transport: HTTPTransportWithTLSClientConfig(InsecureSkipVerifyTLSConfig()),
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultJSONContentType = "application/json; encoding=utf-8"
|
||||
)
|
||||
|
||||
// WriteJSON marshals the provided data as JSON and writes it to the provided
|
||||
// http.ResponseWriter using the provided HTTP status code and content-type. the
|
||||
// nature of this function is that it always writes a HTTP response header. Thus
|
||||
// it makes no sense to write another header on error. Resulting errors should
|
||||
// be logged and the connection should be closes as it is non-functional.
|
||||
func WriteJSON(rw http.ResponseWriter, code int, data interface{}, contentType string) error {
|
||||
if contentType == "" {
|
||||
rw.Header().Set("Content-Type", defaultJSONContentType)
|
||||
} else {
|
||||
rw.Header().Set("content-Type", contentType)
|
||||
}
|
||||
|
||||
rw.WriteHeader(code)
|
||||
|
||||
enc := json.NewEncoder(rw)
|
||||
enc.SetIndent("", " ")
|
||||
|
||||
return enc.Encode(data)
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// OriginFromRequestHeaders tries to find information about the origin from the
|
||||
// provided http.Header. It first looks into the Origin header field and if that
|
||||
// is not found it looks into the Referer header field. If both are not found
|
||||
// an empty string is returned.
|
||||
func OriginFromRequestHeaders(header http.Header) string {
|
||||
origin := header.Get("Origin")
|
||||
if origin == "" {
|
||||
referer := header.Get("Referer")
|
||||
if referer != "" {
|
||||
refererURI, _ := url.Parse(referer)
|
||||
origin = refererURI.Scheme + "://" + refererURI.Host
|
||||
}
|
||||
}
|
||||
|
||||
return origin
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
// WriteRedirect crates a URL out of the provided uri and params and writes a
|
||||
// a HTTP response with the provided HTTP status code to the provided
|
||||
// http.ResponseWriter incliding HTTP caching headers to prevent caching. If
|
||||
// asFragment is true, the provided params are added as URL fragment, otherwise
|
||||
// they replace the query. If params is nil, the provided uri is taken as is.
|
||||
func WriteRedirect(rw http.ResponseWriter, code int, uri *url.URL, params interface{}, asFragment bool) error {
|
||||
if params != nil {
|
||||
paramValues, err := query.Values(params)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
u, _ := url.Parse(uri.String())
|
||||
if asFragment {
|
||||
if u.Fragment != "" {
|
||||
u.Fragment += "&"
|
||||
}
|
||||
f := paramValues.Encode() // This encods into URL encoded form with QueryEscape.
|
||||
f, _ = url.QueryUnescape(f) // But we need it unencoded since its the fragment, it is encoded later (when serializing the URL).
|
||||
u.Fragment += f // Append fragment extension.
|
||||
} else {
|
||||
queryValues := u.Query()
|
||||
for k, vs := range paramValues {
|
||||
for _, v := range vs {
|
||||
queryValues.Add(k, v)
|
||||
}
|
||||
}
|
||||
u.RawQuery = strings.ReplaceAll(queryValues.Encode(), "+", "%20") // NOTE(longsleep): Ensure we use %20 instead of +.
|
||||
}
|
||||
uri = u
|
||||
}
|
||||
|
||||
rw.Header().Set("Location", uri.String())
|
||||
rw.Header().Set("Cache-Control", "no-store")
|
||||
rw.Header().Set("Pragma", "no-cache")
|
||||
|
||||
rw.WriteHeader(code)
|
||||
|
||||
return nil
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"github.com/gorilla/schema"
|
||||
)
|
||||
|
||||
// Create a Decoder instance as a package global, because it caches
|
||||
// meta-data about structs, and an instance can be shared safely.
|
||||
var urlSchemaDecoder = schema.NewDecoder()
|
||||
|
||||
// DecodeURLSchema decodes request for mdata in to the provided dst url struct.
|
||||
func DecodeURLSchema(dst interface{}, src map[string][]string) error {
|
||||
return urlSchemaDecoder.Decode(dst, src)
|
||||
}
|
||||
|
||||
func init() {
|
||||
urlSchemaDecoder.SetAliasTag("url")
|
||||
urlSchemaDecoder.IgnoreUnknownKeys(true)
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// IsRequestFromTrustedSource checks if the provided requests remote address is
|
||||
// one either one of the provided ips or in one of the provided networks.
|
||||
func IsRequestFromTrustedSource(req *http.Request, ips []*net.IP, nets []*net.IPNet) (bool, error) {
|
||||
ipString, _, err := net.SplitHostPort(req.RemoteAddr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
ip := net.ParseIP(ipString)
|
||||
|
||||
for _, checkIP := range ips {
|
||||
if checkIP.Equal(ip) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, checkNet := range nets {
|
||||
if checkNet.Contains(ip) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2017-2019 Kopano and its licensors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package utils
|
||||
Reference in New Issue
Block a user