Commit 3853e1dc authored by Nicolas Pernoud's avatar Nicolas Pernoud
Browse files

fix: ci

parent 444960a5
Pipeline #24770 passed with stages
in 1 minute and 49 seconds
......@@ -13,7 +13,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v2
with:
go-version: 1.x.x
go-version: 1.18.x
- name: Code Checkout
uses: actions/checkout@v2
with:
......
#
# Ce fichier doit être adapté en fonction du projet en renseignant les variables SONAR_PROJECT_KEY et SONAR_TOKEN dans la configuration graphique du projet (https://forge.grandlyon.com/<CHEMIN_DE_VOTRE_PROJET>/settings/ci_cd)
# La variable SONAR_PROJET_KEY peut être trouvée sur https://sonarqube.forge.grandlyon.com/dashboard en ouvrant le projet et en copiant collant le champ en bas à droite (Project Key)
#
# La variable SONAR_TOKEN doit être générée par le responsable du projet depuis son interface sonar : https://sonarqube.forge.grandlyon.com/account/security/
#
image: docker:git
services:
- docker:dind
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: ""
SONAR_URL: https://sonarqube.forge.grandlyon.com
GIT_STRATEGY: clone
GIT_DEPTH: 0
SONAR_URL: http://sonarqube.forge.grandlyon.com:9000
stages:
- sonar-analysis
- build
sonarqube:
stage: sonar-analysis
image: skilldlabs/sonar-scanner:3.4.0
sonarqube-check:
image:
name: sonarsource/sonar-scanner-cli:4
entrypoint: [""]
variables:
SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" # Defines the location of the analysis task cache
GIT_DEPTH: "0" # Tells git to fetch all the branches of the project, required by the analysis task
cache:
key: "${CI_JOB_NAME}"
paths:
- .sonar/cache
script:
- >
sonar-scanner
-Dsonar.projectName=${CI_PROJECT_NAME}
-Dsonar.projectVersion=1.0
-Dsonar.sourceEncoding=UTF-8
-Dsonar.projectBaseDir=.
-Dsonar.host.url=${SONAR_URL}
-Dsonar.projectKey=${SONAR_PROJECT_KEY}
-Dsonar.login=${SONAR_TOKEN}
-Dsonar.organization="default-organization"
- sonar-scanner -Dsonar.login=${SONAR_TOKEN} -Dsonar.projectName=${CI_PROJECT_NAME} -Dsonar.projectKey=${SONAR_PROJECT_KEY} -Dsonar.qualitygate.wait=true -Dsonar.host.url=${SONAR_URL}
allow_failure: true
only:
- master
tags:
- build-push-to-registry
build-master:
stage: build
services:
- docker:dind
before_script:
- docker logout
- docker --version
script:
- echo "+++++++ DÉBUT de construction de l'image Docker ++++++"
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- DOCKER_BUILDKIT=1 docker build --pull -t "$CI_REGISTRY_IMAGE" .
- docker push "$CI_REGISTRY_IMAGE"
- echo "+++++++ FIN de construction de l'image Docker ++++++"
tags:
- build-push-to-registry
only:
- master
build:
stage: build
services:
- docker:dind
before_script:
- docker logout
- docker --version
script:
- echo "+++++++ DÉBUT de construction de l'image Docker ++++++"
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
- DOCKER_BUILDKIT=1 docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG" .
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG"
- echo "+++++++ FIN de construction de l'image Docker ++++++"
tags:
- build-push-to-registry
except:
- master
......@@ -4,7 +4,7 @@
# STEP 1 build executable binary #
##################################
FROM golang:alpine as builder
FROM golang:1.18-alpine3.15 as builder
# Install git + SSL ca certificates.
# Git is required for fetching the dependencies.
......@@ -32,8 +32,7 @@ RUN chown -Rf "${UID}" ./*
# Get dependencies and run tests
RUN go version
RUN go get -d -v && \
CGO_ENABLED=0 go test ./...
RUN go get -d -v && CGO_ENABLED=0 go test ./...
# Build the binary
RUN CGO_ENABLED=0 go build \
......@@ -67,4 +66,4 @@ COPY --from=builder /app/configs /app/configs
USER appuser:appuser
# Run the binary
ENTRYPOINT ["./vestibule"]
\ No newline at end of file
ENTRYPOINT ["./vestibule"]
......@@ -83,6 +83,7 @@ Configuration is done through environment variables. The meaning of the differen
| AUTH_URL | IdP's authentication URL | |
| TOKEN_URL | IdP's token URL | |
| USERINFO_URL | IdP's userinfo URL | |
| ISSUER | IdP's issuer for autoconfiguration of AUTH_URL, TOKEN_URL, USERINFO_URL if they are not already set |
| LOGOUT_URL | IdP's logout URL | |
| ONLYOFFICE_TITLE | Title used on the OnlyOffice document editor window | VestibuleOffice |
| ONLYOFFICE_SERVER | Url of the OnlyOffice document server used to edit documents | |
......@@ -109,7 +110,7 @@ Every branding asset is in `web/assets/brand` directory. They can be altered acc
### Update dependencies
```bash
go get -u
go get -u -t ./...
go mod tidy
```
......@@ -127,6 +128,16 @@ git remote set-url --add --push origin https://github.com/nicolaspernoud/Vestibu
git fetch --all
```
## Update master from development and set development to follow master
```bash
git checkout master
git merge development --squash
# Alter commit message and commit
git checkout development
git reset --hard master
```
## Credits
Loosely based on Webfront (https://github.com/nf/webfront), by Andrew Gerrand, Google (Apache License, Version 2.0).
......
module github.com/nicolaspernoud/vestibule
go 1.17
go 1.18
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/oschwald/maxminddb-golang v1.8.0
github.com/secure-io/sio-go v0.3.1
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd
golang.org/x/net v0.0.0-20220225172249-27dd8689420f
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.27.1 // indirect
......
......@@ -134,8 +134,10 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd h1:XcWmESyNjXJMLahc3mqVQJcgSTDxFxhETVlfk9uGc38=
golang.org/x/crypto v0.0.0-20220315160706-3147a52a75dd/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
......@@ -192,16 +194,20 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167 h1:eDd+TJqbgfXruGQ5sJRU7tEtp/58OAx4+Ayjxg4SM+4=
golang.org/x/net v0.0.0-20210924151903-3ad01bbaa167/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211111083644-e5c967477495 h1:cjxxlQm6d4kYbhpZ2ghvmI8xnq0AG+jXmzrhzfkyu5A=
golang.org/x/net v0.0.0-20211111083644-e5c967477495/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f h1:oA4XRj0qtSt8Yo1Zms0CUlsT3KG69V2UGQWPBxujDmc=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f h1:Qmd2pbz05z7z6lm0DrgQVVPuBm92jqujBKMHMOlOQEw=
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg=
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a h1:qfl7ob3DIEs3Ml9oLuPwY2N04gymzAW04WsUQHIClgM=
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
......@@ -236,18 +242,18 @@ golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665 h1:QOQNt6vCjMpXE7JSK5VvAzJC1byuN3FgTNSBwf+CJgI=
golang.org/x/sys v0.0.0-20210925032602-92d5a993a665/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8 h1:OH54vjqzRWmbJ62fjuhxy7AxFFgoHN0/DPc/UrL8cAs=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
......
......@@ -23,13 +23,44 @@ func Init(portFromMain int) {
func CreateMockOAuth2() *http.ServeMux {
mux := http.NewServeMux()
// Returns authorization code back to the user
mux.HandleFunc("/auth", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`
{
"response_types_supported": [
"code",
"id_token",
"token",
"code id_token",
"code token",
"token id_token",
"code id_token token"
],
"request_parameter_supported": true,
"request_uri_parameter_supported": false,
"jwks_uri": "http://127.0.0.1:8090/jwk",
"subject_types_supported": [
"public"
],
"id_token_signing_alg_values_supported": [
"RS512"
],
"registration_endpoint": "http://127.0.0.1:8090/register",
"issuer": "http://127.0.0.1:8090",
"authorization_endpoint": "http://127.0.0.1:8090/authorize",
"token_endpoint": "http://127.0.0.1:8090/token",
"userinfo_endpoint": "http://127.0.0.1:8090/userinfo"
}
`))
})
// Returns authorization code back to the user
mux.HandleFunc("/authorize", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
redir := query.Get("redirect_uri") + "?state=" + query.Get("state") + "&code=mock_code"
http.Redirect(w, r, redir, http.StatusFound)
})
// Returns authorization code back to the user, but without the provided state
mux.HandleFunc("/auth-wrong-state", func(w http.ResponseWriter, r *http.Request) {
mux.HandleFunc("/authorize-wrong-state", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
redir := query.Get("redirect_uri") + "?state=" + "a-random-state" + "&code=mock_code"
http.Redirect(w, r, redir, http.StatusFound)
......@@ -83,10 +114,7 @@ func CreateMockAPI() *http.ServeMux {
w.Header().Set("Content-Type", "application/json")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Content-Security-Policy", "default-src 'self'; frame-ancestors http://www.example.com")
w.Write([]byte(`{
"foo": "bar",
"bar": "foo"
}`))
w.Write([]byte(`{"foo": "bar","bar": "foo"}`))
})
}(), hostname, port))
return mux
......
......@@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"os"
"strings"
"github.com/nicolaspernoud/vestibule/pkg/appserver"
......@@ -45,6 +46,9 @@ func CreateRootMux(hostname string, port int, appsFile string, davsFile string,
mainMux := http.NewServeMux()
// ALL USERS API ENDPOINTS
// Authentication endpoints
if i, e := os.LookupEnv("ISSUER"); e {
auth.InitIdPEnv(i)
}
m := auth.NewManager()
mainMux.HandleFunc("/OAuth2Login", m.HandleOAuth2Login)
mainMux.Handle("/OAuth2Callback", m.HandleOAuth2Callback())
......@@ -78,8 +82,11 @@ func CreateRootMux(hostname string, port int, appsFile string, davsFile string,
adminMux.HandleFunc("/users/", auth.ProcessUsers)
adminMux.HandleFunc("/sysinfo/", sysinfo.GetInfo)
mainMux.Handle("/api/admin/", http.StripPrefix("/api/admin", auth.ValidateAuthMiddleware(adminMux, []string{auth.AdminRole}, true)))
// Serve static files falling back to serving index.html
mainMux.Handle("/", middlewares.NoCache(http.FileServer(&common.FallBackWrapper{Assets: http.Dir(staticDir)})))
// Serve assets with cache enabled
static := http.FileServer(&common.FallBackWrapper{Assets: http.Dir(staticDir)})
mainMux.Handle("/assets/", static)
// Serve static files falling back to serving index.html with cache disabled
mainMux.Handle("/", middlewares.NoCache(static))
// Put it together into the main handler
mux := http.NewServeMux()
mux.Handle(hostname+"/", middlewares.WebSecurity(mainMux, "*."+hostname+":*", false))
......
......@@ -49,9 +49,9 @@ func TestAll(t *testing.T) {
os.Setenv("USERINFO_URL", oAuth2Server.URL+"/userinfo")
os.Setenv("LOGOUT_URL", oAuth2Server.URL+"/logout")
// Set up testers
os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth-wrong-state") // Set the server to access failing OAuth2 endpoints
os.Setenv("AUTH_URL", oAuth2Server.URL+"/authorize-wrong-state") // Set the server to access failing OAuth2 endpoints
oauth2Tests := createOauth2Tests(t)
os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth") // Set the server to access the correct OAuth2Endpoint
os.Setenv("AUTH_URL", oAuth2Server.URL+"/authorize") // Set the server to access the correct OAuth2Endpoint
unloggedTests := createUnLoggedTests(t)
userTests := createUserTests(t)
os.Setenv("USERINFO_URL", oAuth2Server.URL+"/admininfo")
......@@ -355,5 +355,5 @@ func createTester(t *testing.T) (*httptest.Server, tester.DoFn, tester.DoFn) {
// Create the cookie jar
jar, _ := cookiejar.New(nil)
// wrap the testing function
return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil)
return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar, true), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil, true)
}
This diff is collapsed.
......@@ -15,8 +15,8 @@
"license": "AGPL-3.0-or-later",
"devDependencies": {
"bulma": "^0.9.3",
"clean-css-cli": "^5.3.0",
"node-sass": "^6.0.0",
"clean-css-cli": "^5.5.0",
"node-sass": "^7.0.1",
"rimraf": "^3.0.2"
}
}
......@@ -13,6 +13,7 @@ import (
"time"
"github.com/nicolaspernoud/vestibule/pkg/common"
"github.com/nicolaspernoud/vestibule/pkg/log"
"github.com/nicolaspernoud/vestibule/pkg/tokens"
)
......@@ -24,6 +25,43 @@ const (
ContextData key = 0
)
type OpenIDConfiguration struct {
ResponseTypesSupported []string `json:"response_types_supported"`
RequestParameterSupported bool `json:"request_parameter_supported"`
RequestURIParameterSupported bool `json:"request_uri_parameter_supported"`
JwksURI string `json:"jwks_uri"`
SubjectTypesSupported []string `json:"subject_types_supported"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported"`
RegistrationEndpoint string `json:"registration_endpoint"`
Issuer string `json:"issuer"`
AuthorizationEndpoint string `json:"authorization_endpoint"`
TokenEndpoint string `json:"token_endpoint"`
UserinfoEndpoint string `json:"userinfo_endpoint"`
}
// InitEnv will initialize environment variables from the issuer environment variable
func InitIdPEnv(idpUrl string) {
// Perform a request on the .well-known/oidc-configuration endpoint
r, err := http.Get(idpUrl + "/.well-known/openid-configuration")
if err != nil {
log.Logger.Fatalf("Could not read IdP configuration, exiting...")
}
defer r.Body.Close()
// Unmarshall the response into a struct
var o = OpenIDConfiguration{}
json.NewDecoder(r.Body).Decode(&o)
// Init the other env variables from this struct
if _, e := os.LookupEnv("AUTH_URL"); !e {
os.Setenv("AUTH_URL", o.AuthorizationEndpoint)
}
if _, e := os.LookupEnv("TOKEN_URL"); !e {
os.Setenv("TOKEN_URL", o.TokenEndpoint)
}
if _, e := os.LookupEnv("USERINFO_URL"); !e {
os.Setenv("USERINFO_URL", o.UserinfoEndpoint)
}
}
var (
// AdminRole represents the role reserved for admins
AdminRole = common.StringValueFromEnv("ADMIN_ROLE", "ADMINS")
......@@ -214,7 +252,7 @@ func GetTokenData(r *http.Request) (TokenData, error) {
// isWebdav works out if an user agent is a webdav user agent
func isWebdav(ua string) bool {
for _, a := range []string{"vfs", "Microsoft-WebDAV", "Konqueror", "LibreOffice", "Rei.Fs.WebDAV"} {
for _, a := range []string{"vfs", "Microsoft-WebDAV", "Konqueror", "LibreOffice", "Rei.Fs.WebDAV", "Documents"} {
if strings.Contains(ua, a) {
return true
}
......
......@@ -103,8 +103,8 @@ func (i *FallBackWrapper) Open(name string) (http.File, error) {
return i.Assets.Open("index.html")
}
// Contains works out if a string slice contains a given string element
func Contains(a []string, x string) bool {
// Contains works out if a slice contains a given element
func Contains[K comparable](a []K, x K) bool {
for _, n := range a {
if x == n {
return true
......
......@@ -19,7 +19,7 @@ func TestEncryption(t *testing.T) {
url, _ := url.Parse(ts.URL)
port := url.Port()
// wrap the testing function
do := tester.CreateServerTester(t, port, "vestibule.io", nil)
do := tester.CreateServerTester(t, port, "vestibule.io", nil, true)
var noH map[string]string
// Try to access a crypted file on a encrypted unsecured dav (must pass)
do("PUT", "/test-ciphered.txt", noH, "content is encrypted !", 201, "")
......@@ -74,7 +74,7 @@ func TestSetModTime(t *testing.T) {
ts := httptest.NewServer(&davAug)
url, _ := url.Parse(ts.URL)
port := url.Port()
doPlain := tester.CreateServerTester(t, port, "vestibule.io", nil)
doPlain := tester.CreateServerTester(t, port, "vestibule.io", nil, true)
test(doPlain)
// Test with encrypted webdav
......@@ -82,6 +82,6 @@ func TestSetModTime(t *testing.T) {
tsEnc := httptest.NewServer(&davAugEnc)
urlEnc, _ := url.Parse(tsEnc.URL)
portEnc := urlEnc.Port()
doEnc := tester.CreateServerTester(t, portEnc, "vestibule.io", nil)
doEnc := tester.CreateServerTester(t, portEnc, "vestibule.io", nil, true)
test(doEnc)
}
......@@ -56,11 +56,16 @@ func CloseFile() {
// GetCityAndCountryFromRequest returns a string containing the city and the contry where the request is from
// If the city is fetched from the built-in memory cache, it is NOT suffixed by a dot (.)
func GetCityAndCountryFromRequest(req *http.Request) string {
// If the request remote adress is local return "localhost"
// If the request remote adress is the host itself return "localhost"
if req.RemoteAddr == "" || strings.HasPrefix(req.RemoteAddr, "[::1]") || strings.HasPrefix(req.RemoteAddr, "127.0.0.1") {
return "localhost"
}
// If the request remote adress is from local network return "local network"
if strings.HasPrefix(req.RemoteAddr, "192.168.") {
return "local network"
}
// Lock the cache
ipcache.mux.Lock()
defer ipcache.mux.Unlock()
......
......@@ -10,8 +10,14 @@ import (
func TestGetCityAndCountryFromRequest(t *testing.T) {
// Request types
requestFromLocalHost := httptest.NewRequest("GET", "/test", strings.NewReader(""))
requestFromLocalHost.RemoteAddr = "[::1]:1234"
requestFromLocalHostV6 := httptest.NewRequest("GET", "/test", strings.NewReader(""))
requestFromLocalHostV6.RemoteAddr = "[::1]:1234"
requestFromLocalHostV4 := httptest.NewRequest("GET", "/test", strings.NewReader(""))
requestFromLocalHostV4.RemoteAddr = "127.0.0.1:1234"
requestFromLocalNetworkV4 := httptest.NewRequest("GET", "/test", strings.NewReader(""))
requestFromLocalNetworkV4.RemoteAddr = "192.168.1.1:1234"
requestFromLondon := httptest.NewRequest("GET", "/test", strings.NewReader(""))
requestFromLondon.RemoteAddr = "81.2.69.142:1234"
......@@ -61,12 +67,26 @@ func TestGetCityAndCountryFromRequest(t *testing.T) {
want string
}{
{
name: "Request from localhost",
name: "Request from localhost ipv4",
args: args{
req: requestFromLocalHost,
req: requestFromLocalHostV4,
},
want: "localhost",
},
{
name: "Request from localhost ipv6",
args: args{
req: requestFromLocalHostV6,
},
want: "localhost",
},
{
name: "Request from local network",
args: args{
req: requestFromLocalNetworkV4,
},
want: "local network",
},
{
name: "Request from london",
args: args{
......
......@@ -36,7 +36,7 @@ func DoRequestOnHandler(t *testing.T, router http.Handler, method string, route
}
// DoRequestOnServer does a request on listening server
func DoRequestOnServer(t *testing.T, hostname string, port string, jar *cookiejar.Jar, method string, testURL string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string {
func DoRequestOnServer(t *testing.T, hostname string, port string, jar *cookiejar.Jar, method string, testURL string, headers map[string]string, payload string, expectedStatus int, expectedBody string, followRedirects bool) string {
dialer := &net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
......@@ -63,11 +63,14 @@ func DoRequestOnServer(t *testing.T, hostname string, port string, jar *cookieja
for i, v := range headers {
req.Header.Set(i, v)
}
var client *http.Client
client := &http.Client{}
if !followRedirects {
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
}
if jar != nil {
client = &http.Client{Jar: jar}
} else {
client = &http.Client{}
client.Jar = jar
}
res, err := client.Do(req)
if err != nil {
......@@ -85,8 +88,8 @@ func DoRequestOnServer(t *testing.T, hostname string, port string, jar *cookieja
}
// CreateServerTester wraps DoRequestOnServer to factorize t, port and jar
func CreateServerTester(t *testing.T, hostname string, port string, jar *cookiejar.Jar) DoFn {
func CreateServerTester(t *testing.T, hostname string, port string, jar *cookiejar.Jar, followRedirects bool) DoFn {
return func(method string, url string, headers map[string]string, payload string, expectedStatus int, expectedBody string) string {
return DoRequestOnServer(t, port, hostname, jar, method, url, headers, payload, expectedStatus, expectedBody)
return DoRequestOnServer(t, port, hostname, jar, method, url, headers, payload, expectedStatus, expectedBody, followRedirects)
}
}
......@@ -37,6 +37,19 @@ func Init(keyfile string, debug bool) {
m = newManager(keyfile, debug)
}
// InitFromString inits the main token manager from a given string
func InitFromString(key string, debug bool) {
mKey := []byte(key)
if len(mKey) == 0 {
var err error
mKey, err = common.GenerateRandomBytes(32)
if err != nil {
log.Logger.Fatal(err)
}
}
m = manager{key: mKey, debugMode: debug}
}
// newManager creates a manager
func newManager(keyfile string, debug bool) manager {
var keyConfig struct {
......
......@@ -200,10 +200,10 @@ class Apps {
<hr class="dropdown-divider" />
<div class="dropdown-item">
<p><strong>${app.host}</strong></p>
<p>${app.isProxy ? "Proxies to <strong>" + app.forwardTo + "</strong>" : "Serves <strong>" + app.serve + "</strong>"}</p>
<p>${app.secured ? "Restricted access to users with roles <strong>" + app.roles + "</strong>" : "Unrestricted access"}</p>