Compare commits

...

26 Commits

Author SHA1 Message Date
8ded23cccc Merge pull request 'Définition des claims amr et acr via la configuration' (#5) from acr_amr into develop
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Reviewed-on: #5
Reviewed-by: Laurent Gourvenec <lgourvenec@cadoles.com>
2025-02-17 16:44:21 +01:00
46b279a4f0 feat: add configurable acr/amr claims
Some checks are pending
Cadoles/hydra-werther/pipeline/pr-develop Build started...
2025-02-17 16:38:07 +01:00
15a47179f4 feat: add release message with docker pull command
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
2023-12-06 15:21:30 +01:00
7edc889271 Merge pull request 'Construction et diffusion des paquets/image de conteneur via Jenkins' (#4) from jenkins-release into develop
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Reviewed-on: #4
2023-12-06 14:47:27 +01:00
b3a3e1987d feat: release docker and packages from jenkins
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Cadoles/hydra-werther/pipeline/pr-develop This commit looks good
2023-12-06 14:43:28 +01:00
d74f81224d chore: update templates metadata
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
2023-12-06 12:02:29 +01:00
885d18ebb0 Merge pull request 'Possibilité d'ignorer les vérifications TLS sur les connexions au service Hydra depuis la configuration' (#3) from feat/ssl-ignore into develop
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Reviewed-on: #3
2023-12-06 11:55:57 +01:00
271d62dc27 feat: configurable ignore of tls verification for hydra connections
All checks were successful
Cadoles/hydra-werther/pipeline/pr-develop This commit looks good
2023-12-06 11:55:01 +01:00
592749eebf Merge pull request 'Délai de connexion au serveur LDAP configurable' (#2) from ldap-configurable-timeout into develop
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Reviewed-on: #2
2023-12-06 11:45:30 +01:00
24b66a12ef feat: add configurable ldap connection timeout
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
Cadoles/hydra-werther/pipeline/pr-develop This commit looks good
2023-12-06 11:43:58 +01:00
194c1864c4 fix: configuration path in package
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
2022-11-24 15:32:33 -06:00
b940aae071 chore: add nfpm based packing recipe
All checks were successful
Cadoles/hydra-werther/pipeline/head This commit looks good
2022-11-03 15:30:40 -06:00
eab0b72431 chore: add generate command to updates embedded assets 2021-09-27 16:22:10 +02:00
3525b4bcb5 fix: trim login url when displaying error message 2021-09-27 16:21:33 +02:00
138e818429 Send all retrieved groups 'as-is' in claims 2021-09-24 15:54:10 +02:00
c7599a8faa chore: allow build of only specific distribution without error 2021-09-17 11:46:07 +02:00
bd2c94fc15 chore: add debian packaging recipe 2021-09-17 11:33:17 +02:00
fb981c5df9 chore: add local build helpers 2021-09-17 11:09:53 +02:00
2ad1595a34 ldapclient: allow search queries customisation in configuration 2021-09-17 11:08:16 +02:00
938d1939d4 fix logout_test.go 2021-08-09 12:40:57 +03:00
5ea0476107 fixes and casing 2021-08-09 12:40:57 +03:00
cd8a983bdd forgot one 2021-08-09 12:40:57 +03:00
6a4ab470b4 add to acceptRequest 2021-08-09 12:40:57 +03:00
67c63ca8cd enable fake TLS termination 2021-08-09 12:40:57 +03:00
9f8461f71a build: update github api key for travis 2021-03-30 16:37:26 +03:00
91691f2a6a build: update github api key for travis 2021-03-30 16:37:26 +03:00
26 changed files with 540 additions and 100 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/bin
/dist
/tools
/.trivy
.mktools/

View File

@ -56,7 +56,7 @@ before_deploy:
deploy:
- provider: releases
api_key:
secure: f3KYUKsrtYRKcttPfWmHWGCFT2ugas1fgbBACGCTFp/ir6AAfHt16FYHgyfXc8+V9IajNkwqL6TrDjQFfLSI0qx38s+wLK6FIicjvMzVWXe/lCHByr4kAZuN2BOynrKiE8rkEcXHGjX2adrrvETNwUCp+oIxQtdIAVvE1Fjkb+MdnCs7Ed9beggqNOJksGVJ44X17ezarCc8/L4ULIXY/OBLnUnwH6UwMrSIPuwvMJAZlhyJWOv4ro8Z3D2f5vfD91MNit8rCkXkYPKnw1/rIpBbaoARLQ95bN97NsLkfeNlSgoAXhy00i+Jz78PgD3TvTPecdlTPwBNNnHaXBtQZjB+qHpr4lW/NP2+IJ6Aku8JY2X+Srd/BYD8hh4Nqzp3UiymsQS61++jfZmi3xUu5nhFkd+MavVW8Xy0/8vnREqHuwCQ8+oo1GHqDnKgdeRMm29AwTTx/FyUSPlzWzQIC1PVFtS3/YYqAn7sooS6l5MuSENk05IYOM1ApXOGb6tNW8wDGTD8QP8KvJjfARg8365wwhEAP6gdrW6VotSjY5XZM37ge0uKfKBvw8BVNfbn/R4/12KIuqPsEmbVfFJx18DQzz3b+9UfPZQwuxZvgNnngplUbzP2q/cKYNSMHKzZ53EVPPr5wtdDWm5pnbLtWbrN5d+y2FoS+YBrCrL09C8=
secure: X0diTlyWbycImd8x1ce1VHC9IcaPD9f0Pl9ynYHoV0BNw4KrgGD6OdiOM7Z7fiZA8jly0jh81orxdm3o7hIAlX02BA2kg8BFi83dcm01pcwW5vZZq0w/XMMtw644O8CAImPr58YKUjBb7c1+RENjPqjIZrdjVDzeRu0k5oOaDpk94016B1j1OB9XtkXzPzgP+KTx5gCmfeipQ6LxbL8wzXEqJWGF6+5B/7bqkzSw7vIvDtYlYgM7jf5NZslxiKlVr7pkRik6KlfRjtt/pi4ZJwpcu4AHdnNZoXkcmQwz21yd1lVFUbqNi8qGJyilOQB+p1RvZ/c5Q7a+FHN9anUM06DtfL/bMII+Kqt429M4sk9mA3rEp61SkDML1o9lJ8iiEKXPxMNtWKv1I/ixrZWYPE3g75fTaoyBusBS1bxNtOgAUf2dIr0JCO+p2NTaCyd0vOr87/KMVnJTeLzOnisMY0DOjQ3FhYmXSRPtyJF72jDxh338YX7hHK5DcwL9aVKOA5t7VbM2RW6VdavH5kHUPDEbZaDFlsUcncHJnzD1QtX7ODJh7EgFteE32fmJFBcE2BAWDIvYy68sCNU64Wwh2yBCbnMzLhET/nLm3Jj4U6Jl3VqAT2ff0UQBgugW0OpxO7JBW4b3dNwmKKA6WE5+WitO8EzE8sA09tnIEIe7YT8=
file:
- bin/werther_linux_386.tar.gz
- bin/werther_linux_amd64.tar.gz

View File

@ -3,7 +3,7 @@
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
FROM golang:1.13-alpine AS build
FROM golang:1.21-alpine AS build
ARG VERSION
ARG GOPROXY

29
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,29 @@
@Library('cadoles') _
// Utilisation du pipeline "standard"
// Voir https://forge.cadoles.com/Cadoles/Jenkins/src/branch/master/doc/tutorials/standard-make-pipeline.md
standardMakePipeline([
'dockerfileExtension': '''
RUN apt-get update \
&& apt-get install -y zip jq
RUN wget https://go.dev/dl/go1.21.5.linux-amd64.tar.gz \
&& rm -rf /usr/local/go \
&& tar -C /usr/local -xzf go1.21.5.linux-amd64.tar.gz
ENV PATH="${PATH}:/usr/local/go/bin"
''',
'hooks': [
'pre-release': {
// Login into docker registry
sh '''
make .mktools
echo "$MKT_GITEA_RELEASE_PASSWORD" | docker login --username "$MKT_GITEA_RELEASE_USERNAME" --password-stdin reg.cadoles.com
'''
}
],
// Use credentials to push images to registry and pubish gitea release
'credentials': [
usernamePassword(credentialsId: 'kipp-credentials', usernameVariable: 'MKT_GITEA_RELEASE_USERNAME', passwordVariable: 'MKT_GITEA_RELEASE_PASSWORD')
]
])

86
Makefile Normal file
View File

@ -0,0 +1,86 @@
SHELL := /bin/bash
IMAGE_NAME := reg.cadoles.com/cadoles/hydra-werther
NFPM_VERSION ?= 2.20.0
NFPM_PACKAGERS ?= deb rpm
MKT_GITEA_RELEASE_ORG ?= Cadoles
MKT_GITEA_RELEASE_PROJECT ?= hydra-werther
MKT_GITEA_RELEASE_VERSION ?= $(MKT_PROJECT_VERSION)
define MKT_GITEA_RELEASE_BODY
## Docker usage
```
docker pull $(IMAGE_NAME):$(MKT_PROJECT_VERSION)
```
endef
export MKT_GITEA_RELEASE_BODY
build: build-bin build-image
build-bin: clean generate
CGO_ENABLED=0 misc/script/build
test: scan
generate:
go generate ./...
clean:
rm -rf bin dist
dist:
mkdir -p dist
package: clean build-bin $(foreach p,$(NFPM_PACKAGERS), package-$(p))
package-%: dist tools/nfpm/bin/nfpm
PACKAGE_VERSION=$(MKT_PROJECT_VERSION) \
tools/nfpm/bin/nfpm package \
--config misc/packaging/nfpm.yml \
--target ./dist \
--packager $*
tools/nfpm/bin/nfpm:
mkdir -p tools/nfpm/bin
curl -L --output tools/nfpm/nfpm_$(NFPM_VERSION)_Linux_x86_64.tar.gz https://github.com/goreleaser/nfpm/releases/download/v$(NFPM_VERSION)/nfpm_$(NFPM_VERSION)_Linux_x86_64.tar.gz \
&& tar -xzf tools/nfpm/nfpm_$(NFPM_VERSION)_Linux_x86_64.tar.gz -C tools/nfpm/bin \
&& chmod +x tools/nfpm/bin/nfpm \
&& rm -f tools/nfpm/nfpm_$(NFPM_VERSION)_Linux_x86_64.tar.gz
build-image:
docker build \
-t "${IMAGE_NAME}:latest" \
.
scan: build-image tools/trivy/bin/trivy
mkdir -p .trivy
tools/trivy/bin/trivy --cache-dir .trivy/.cache image --ignorefile .trivyignore.yaml $(TRIVY_ARGS) $(IMAGE_NAME):latest
tools/trivy/bin/trivy:
mkdir -p tools/trivy/bin
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b ./tools/trivy/bin v0.47.0
release: release-image release-gitea
release-gitea: .mktools package
@[ ! -z "$(MKT_PROJECT_VERSION)" ] || ( echo "Just downloaded mktools. Please re-run command."; exit 1 )
$(MAKE) MKT_GITEA_RELEASE_ATTACHMENTS="$$(find dist/* -type f -printf '%p ')" mkt-gitea-release
release-image: .mktools
@[ ! -z "$(MKT_PROJECT_VERSION)" ] || ( echo "Just downloaded mktools. Please re-run command."; exit 1 )
docker tag "${IMAGE_NAME}:latest" "${IMAGE_NAME}:$(MKT_PROJECT_VERSION)"
docker tag "${IMAGE_NAME}:latest" "${IMAGE_NAME}:$(MKT_PROJECT_SHORT_VERSION)"
docker tag "${IMAGE_NAME}:latest" "${IMAGE_NAME}:$(MKT_PROJECT_VERSION_CHANNEL)-latest"
docker push "${IMAGE_NAME}:$(MKT_PROJECT_VERSION)"
docker push "${IMAGE_NAME}:$(MKT_PROJECT_SHORT_VERSION)"
docker push "${IMAGE_NAME}:$(MKT_PROJECT_VERSION_CHANNEL)-latest"
.mktools:
rm -rf .mktools
curl -q https://forge.cadoles.com/Cadoles/mktools/raw/branch/master/install.sh | TASKS="version gitea" $(SHELL)
-include .mktools/*.mk

View File

@ -14,6 +14,8 @@ import (
"net/url"
"os"
"crypto/tls"
"github.com/i-core/rlog"
"github.com/i-core/routegroup"
"github.com/i-core/werther/internal/identp"
@ -30,11 +32,12 @@ var version = ""
// Config is a server's configuration.
type Config struct {
DevMode bool `envconfig:"dev_mode" default:"false" desc:"a development mode"`
Listen string `default:":8080" desc:"a host and port to listen on (<host>:<port>)"`
Identp identp.Config
LDAP ldapclient.Config
Web web.Config
DevMode bool `envconfig:"dev_mode" default:"false" desc:"Enable development mode"`
Listen string `default:":8080" desc:"a host and port to listen on (<host>:<port>)"`
InsecureSkipVerify bool `envconfig:"insecure_skip_verify" default:"false" desc:"Disable TLS verification on Hydra connection"`
Identp identp.Config
LDAP ldapclient.Config
Web web.Config
}
func main() {
@ -80,6 +83,11 @@ func main() {
os.Exit(1)
}
if cnf.InsecureSkipVerify {
log.Warn("All ssl verifications are disabled !")
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
}
ldap := ldapclient.New(cnf.LDAP)
router := routegroup.NewRouter(nosurf.NewPure, rlog.NewMiddleware(log))

143
conf/hydra-werther.conf Normal file
View File

@ -0,0 +1,143 @@
#WERTHER_DEV_MODE=
# [description] a development mode
# [type] True or False
# [default] false
# [required]
#WERTHER_LISTEN=
# [description] a host and port to listen on (<host>:<port>)
# [type] String
# [default] :8080
# [required]
WERTHER_IDENTP_HYDRA_URL=http://localhost:4445/
# [description] an admin URL of ORY Hydra Server
# [type] String
# [default]
# [required] true
#WERTHER_IDENTP_SESSION_TTL=
# [description] a user session's TTL
# [type] Duration
# [default] 24h
# [required]
#WERTHER_IDENTP_CLAIM_SCOPES=
# [description] a mapping of OpenID Connect claims to scopes (all claims are URL encoded)
# [type] Comma-separated list of String:String pairs
# [default] name:profile,family_name:profile,given_name:profile,email:email,https%3A%2F%2Fgithub.com%2Fi-core%2Fwerther%2Fclaims%2Froles:roles
# [required]
WERTHER_LDAP_ENDPOINTS=localhost:389
# [description] a LDAP's server URLs as "<address>:<port>"
# [type] Comma-separated list of String
# [default]
# [required] true
WERTHER_LDAP_BINDDN=
# [description] a LDAP bind DN
# [type] String
# [default]
# [required]
WERTHER_LDAP_BINDPW=
# [description] a LDAP bind password
# [type] String
# [default]
# [required]
WERTHER_LDAP_BASEDN=ou=users,dc=myorg,dc=com
# [description] a LDAP base DN for searching users
# [type] String
# [default]
# [required] true
#WERTHER_LDAP_USER_SEARCH_QUERY=
# [description] the user search query
# [type] String
# [default] (&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))
# [required]
#WERTHER_LDAP_ATTR_CLAIMS=
# [description] a mapping of LDAP attributes to OpenID connect claims
# [type] Comma-separated list of String:String pairs
# [default] name:name,sn:family_name,givenName:given_name,mail:email
# [required]
WERTHER_LDAP_ROLE_BASEDN=ou=groups,dc=myorg,dc=com
# [description] a LDAP base DN for searching roles
# [type] String
# [default]
# [required] true
#WERTHER_LDAP_ROLE_SEARCH_QUERY=
# [description] the role search query
# [type] String
# [default] (|(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s)))
# [required]
#WERTHER_LDAP_ROLE_ATTR=
# [description] a LDAP group's attribute that contains a role's name
# [type] String
# [default] description
# [required]
#WERTHER_LDAP_ROLE_CLAIM=
# [description] a name of an OpenID Connect claim that contains user roles
# [type] String
# [default] https://github.com/i-core/werther/claims/roles
# [required]
#WERTHER_LDAP_CACHE_SIZE=
# [description] a user info cache's size in KiB
# [type] Integer
# [default] 512
# [required]
#WERTHER_LDAP_CACHE_TTL=
# [description] a user info cache TTL
# [type] Duration
# [default] 30m
# [required]
#WERTHER_LDAP_IS_TLS=
# [description] should LDAP connection be established via TLS
# [type] True or False
# [default] false
# [required]
#WERTHER_WEB_DIR=
# [description] a path to an external web directory
# [type] String
# [default]
# [required]
#WERTHER_WEB_BASE_PATH=
# [description] a base path of web pages
# [type] String
# [default] /
# [required]
#WERTHER_LDAP_CONNECTION_TIMEOUT=
# [description] LDAP server connection timeout
# [type] Duration
# [default] 60s
# [required]
# WERTHER_INSECURE_SKIP_VERIFY=
# [description] Disable TLS verification on Hydra connection
# [type] True or False
# [default] false
# [required]
# WERTHER_IDENTP_AMR=
# [description] Authentication Method Reference Values
# [type] Comma-separated list of String
# [default]
# [required] false
# WERTHER_IDENTP_ACR=
# [description] Authentication Context Class Reference
# [type] String
# [default]
# [required] false

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

14
debian/control vendored Normal file
View File

@ -0,0 +1,14 @@
Source: hydra-werther
Section: unknown
Priority: optional
Maintainer: Cadoles <contact@cadoles.com>
Build-Depends: debhelper (>= 8.0.0), wget, ca-certificates, tar
Standards-Version: 3.9.4
Homepage: http://forge.cadoles.com/Cadoles/hydra-werther
Vcs-Git: http://forge.cadoles.com/Cadoles/hydra-werther.git
Vcs-Browser: http://forge.cadoles.com/Cadoles/hydra-werther
Package: hydra-werther
Architecture: amd64
Depends: ${shlibs:Depends}, ${misc:Depends}, ssl-cert
Description: Hydra identity provider backed by LDAP compatible server

12
debian/hydra-werther.service vendored Normal file
View File

@ -0,0 +1,12 @@
[Unit]
Description=Run Hydra Werther login/consent/logout app
After=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/hydra-werther/hydra-werther.conf
ExecStart=/usr/bin/hydra-werther
Restart=on-failure
[Install]
WantedBy=multi-user.target

40
debian/rules vendored Normal file
View File

@ -0,0 +1,40 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
export DH_VERBOSE=1
GO_VERSION := 1.17.1
OS := linux
ARCH := amd64
GOPATH=$(HOME)/go
ifeq (, $(shell which go 2>/dev/null))
override_dh_auto_build: install-go
endif
%:
dh $@ --with systemd
install-go:
wget https://dl.google.com/go/go$(GO_VERSION).$(OS)-$(ARCH).tar.gz
tar -C /usr/local -xzf go$(GO_VERSION).$(OS)-$(ARCH).tar.gz
override_dh_auto_build: $(GOPATH)
GOPATH=$(GOPATH) PATH="$(PATH):/usr/local/go/bin:$(GOPATH)/bin" DISTS=$(OS)/$(ARCH) make
$(GOPATH):
mkdir -p $(GOPATH)
override_dh_auto_install:
mkdir -p debian/hydra-werther/usr/bin
mkdir -p debian/hydra-werther/etc/hydra-werther
cp bin/werther_$(OS)_$(ARCH) debian/hydra-werther/usr/bin/hydra-werther
cp conf/hydra-werther.conf debian/hydra-werther/etc/hydra-werther
install -d debian/hydra-werther
override_dh_strip:
override_dh_auto_test:

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (native)

23
go.mod
View File

@ -1,11 +1,8 @@
module github.com/i-core/werther
require (
github.com/OneOfOne/xxhash v1.2.2 // indirect
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883
github.com/cespare/xxhash v1.0.0 // indirect
github.com/coocood/freecache v1.0.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/elazarl/go-bindata-assetfs v1.0.0
github.com/go-ldap/ldap/v3 v3.2.3
github.com/i-core/rlog v1.0.0
@ -14,10 +11,24 @@ require (
github.com/kelseyhightower/envconfig v1.3.0
github.com/kevinburke/go-bindata v3.13.0+incompatible
github.com/pkg/errors v0.8.1
github.com/sergi/go-diff v1.0.0 // indirect
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 // indirect
go.uber.org/zap v1.10.0
golang.org/x/text v0.3.2
)
go 1.13
require (
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
github.com/OneOfOne/xxhash v1.2.2 // indirect
github.com/cespare/xxhash v1.0.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
github.com/gofrs/uuid v3.2.0+incompatible // indirect
github.com/julienschmidt/httprouter v1.2.0 // indirect
github.com/justinas/alice v0.0.0-20171023064455-03f45bd4b7da // indirect
github.com/sergi/go-diff v1.0.0 // indirect
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 // indirect
go.uber.org/atomic v1.4.0 // indirect
go.uber.org/multierr v1.1.0 // indirect
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9 // indirect
)
go 1.21

View File

@ -13,18 +13,19 @@ import (
// ConsentReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
type ConsentReqDoer struct {
hydraURL string
rememberFor int
hydraURL string
fakeTLSTermination bool
rememberFor int
}
// NewConsentReqDoer creates a ConsentRequest.
func NewConsentReqDoer(hydraURL string, rememberFor int) *ConsentReqDoer {
return &ConsentReqDoer{hydraURL: hydraURL, rememberFor: rememberFor}
func NewConsentReqDoer(hydraURL string, fakeTLSTermination bool, rememberFor int) *ConsentReqDoer {
return &ConsentReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination, rememberFor: rememberFor}
}
// InitiateRequest fetches information on the OAuth2 request.
func (crd *ConsentReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
ri, err := initiateRequest(consent, crd.hydraURL, challenge)
ri, err := initiateRequest(consent, crd.hydraURL, crd.fakeTLSTermination, challenge)
return ri, errors.Wrap(err, "failed to initiate consent request")
}
@ -46,6 +47,6 @@ func (crd *ConsentReqDoer) AcceptConsentRequest(challenge string, remember bool,
IDToken: idToken,
},
}
redirectURI, err := acceptRequest(consent, crd.hydraURL, challenge, data)
redirectURI, err := acceptRequest(consent, crd.hydraURL, crd.fakeTLSTermination, challenge, data)
return redirectURI, errors.Wrap(err, "failed to accept consent request")
}

View File

@ -55,7 +55,7 @@ func TestInitiateConsentRequest(t *testing.T) {
h := &testInitiateConsentHandler{reqInfo: tc.reqInfo, status: tc.status}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewConsentReqDoer(srv.URL, tc.rememberFor)
ldr := hydra.NewConsentReqDoer(srv.URL, false, tc.rememberFor)
reqInfo, err := ldr.InitiateRequest(tc.challenge)
@ -149,7 +149,7 @@ func TestAcceptConsentRequest(t *testing.T) {
h := &testAcceptConsentHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewConsentReqDoer(srv.URL, tc.rememberFor)
ldr := hydra.NewConsentReqDoer(srv.URL, false, tc.rememberFor)
var grantScope []string
for _, v := range tc.grantScope {

View File

@ -26,6 +26,8 @@ var (
ErrChallengeNotFound = errors.New("challenge not found")
// ErrChallengeExpired is an error that happens when a challenge is already used.
ErrChallengeExpired = errors.New("challenge expired")
//ErrServiceUnavailable is an error that happens when the hydra admin service is unavailable
ErrServiceUnavailable = errors.New("hydra service unavailable")
)
type reqType string
@ -44,7 +46,7 @@ type ReqInfo struct {
Subject string `json:"subject"`
}
func initiateRequest(typ reqType, hydraURL, challenge string) (*ReqInfo, error) {
func initiateRequest(typ reqType, hydraURL string, fakeTLSTermination bool, challenge string) (*ReqInfo, error) {
if challenge == "" {
return nil, ErrChallengeMissed
}
@ -52,13 +54,23 @@ func initiateRequest(typ reqType, hydraURL, challenge string) (*ReqInfo, error)
if err != nil {
return nil, err
}
u, err := parseURL(hydraURL)
if err != nil {
return nil, err
}
u = u.ResolveReference(ref)
resp, err := http.Get(u.String())
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
return nil, err
}
if fakeTLSTermination {
req.Header.Add("X-Forwarded-Proto", "https")
}
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
@ -76,7 +88,7 @@ func initiateRequest(typ reqType, hydraURL, challenge string) (*ReqInfo, error)
return &ri, nil
}
func acceptRequest(typ reqType, hydraURL, challenge string, data interface{}) (string, error) {
func acceptRequest(typ reqType, hydraURL string, fakeTLSTermination bool, challenge string, data interface{}) (string, error) {
if challenge == "" {
return "", ErrChallengeMissed
}
@ -101,6 +113,10 @@ func acceptRequest(typ reqType, hydraURL, challenge string, data interface{}) (s
if err != nil {
return "", err
}
if fakeTLSTermination {
r.Header.Add("X-Forwarded-Proto", "https")
}
r.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(r)
if err != nil {
@ -132,6 +148,8 @@ func checkResponse(resp *http.Response) error {
return ErrChallengeNotFound
case 409:
return ErrChallengeExpired
case 503:
return ErrServiceUnavailable
default:
var rs struct {
Message string `json:"error"`

View File

@ -13,32 +13,39 @@ import (
// LoginReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
type LoginReqDoer struct {
hydraURL string
rememberFor int
hydraURL string
fakeTLSTermination bool
rememberFor int
acr string
amr []string
}
// NewLoginReqDoer creates a LoginRequest.
func NewLoginReqDoer(hydraURL string, rememberFor int) *LoginReqDoer {
return &LoginReqDoer{hydraURL: hydraURL, rememberFor: rememberFor}
func NewLoginReqDoer(hydraURL string, fakeTLSTermination bool, rememberFor int, acr string, amr []string) *LoginReqDoer {
return &LoginReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination, rememberFor: rememberFor, acr: acr, amr: amr}
}
// InitiateRequest fetches information on the OAuth2 request.
func (lrd *LoginReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
ri, err := initiateRequest(login, lrd.hydraURL, challenge)
ri, err := initiateRequest(login, lrd.hydraURL, lrd.fakeTLSTermination, challenge)
return ri, errors.Wrap(err, "failed to initiate login request")
}
// AcceptLoginRequest accepts the requested authentication process, and returns redirect URI.
func (lrd *LoginReqDoer) AcceptLoginRequest(challenge string, remember bool, subject string) (string, error) {
data := struct {
Remember bool `json:"remember"`
RememberFor int `json:"remember_for"`
Subject string `json:"subject"`
Remember bool `json:"remember"`
RememberFor int `json:"remember_for"`
Subject string `json:"subject"`
ACR string `json:"acr,omitempty"`
AMR []string `json:"amr,omitempty"`
}{
Remember: remember,
RememberFor: lrd.rememberFor,
Subject: subject,
ACR: lrd.acr,
AMR: lrd.amr,
}
redirectURI, err := acceptRequest(login, lrd.hydraURL, challenge, data)
redirectURI, err := acceptRequest(login, lrd.hydraURL, lrd.fakeTLSTermination, challenge, data)
return redirectURI, errors.Wrap(err, "failed to accept login request")
}

View File

@ -60,7 +60,7 @@ func TestInitiateLoginRequest(t *testing.T) {
h := &testInitiateLoginHandler{reqInfo: tc.reqInfo, status: tc.status}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewLoginReqDoer(srv.URL, 0)
ldr := hydra.NewLoginReqDoer(srv.URL, false, 0, "", nil)
reqInfo, err := ldr.InitiateRequest(tc.challenge)
@ -160,7 +160,7 @@ func TestAcceptLoginRequest(t *testing.T) {
h := &testAcceptLoginHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewLoginReqDoer(srv.URL, tc.rememberFor)
ldr := hydra.NewLoginReqDoer(srv.URL, false, tc.rememberFor, "", nil)
redirect, err := ldr.AcceptLoginRequest(tc.challenge, tc.remember, tc.subject)

View File

@ -13,22 +13,23 @@ import (
// LogoutReqDoer fetches information on the OAuth2 request and then accepts or rejects the requested logout process.
type LogoutReqDoer struct {
hydraURL string
hydraURL string
fakeTLSTermination bool
}
// NewLogoutReqDoer creates a LogoutRequest.
func NewLogoutReqDoer(hydraURL string) *LogoutReqDoer {
return &LogoutReqDoer{hydraURL: hydraURL}
func NewLogoutReqDoer(hydraURL string, fakeTLSTermination bool) *LogoutReqDoer {
return &LogoutReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination}
}
// InitiateRequest fetches information on the OAuth2 request.
func (lrd *LogoutReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
ri, err := initiateRequest(logout, lrd.hydraURL, challenge)
ri, err := initiateRequest(logout, lrd.hydraURL, lrd.fakeTLSTermination, challenge)
return ri, errors.Wrap(err, "failed to initiate logout request")
}
// AcceptLogoutRequest accepts the requested logout process, and returns redirect URI.
func (lrd *LogoutReqDoer) AcceptLogoutRequest(challenge string) (string, error) {
redirectURI, err := acceptRequest(logout, lrd.hydraURL, challenge, nil)
redirectURI, err := acceptRequest(logout, lrd.hydraURL, lrd.fakeTLSTermination, challenge, nil)
return redirectURI, errors.Wrap(err, "failed to accept logout request")
}

View File

@ -44,7 +44,7 @@ func TestInitiateLogoutRequest(t *testing.T) {
h := &testInitiateLogoutHandler{reqInfo: tc.reqInfo, status: tc.status}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewLogoutReqDoer(srv.URL)
ldr := hydra.NewLogoutReqDoer(srv.URL, false)
reqInfo, err := ldr.InitiateRequest(tc.challenge)
@ -126,7 +126,7 @@ func TestAcceptLogoutRequest(t *testing.T) {
h := &testAcceptLogoutHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
srv := httptest.NewServer(h)
defer srv.Close()
ldr := hydra.NewLogoutReqDoer(srv.URL)
ldr := hydra.NewLogoutReqDoer(srv.URL, false)
redirect, err := ldr.AcceptLogoutRequest(tc.challenge)

View File

@ -11,6 +11,7 @@ package identp
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
@ -27,9 +28,12 @@ const loginTmplName = "login.tmpl"
// Config is a Hydra configuration.
type Config struct {
HydraURL string `envconfig:"hydra_url" required:"true" desc:"an admin URL of ORY Hydra Server"`
SessionTTL time.Duration `envconfig:"session_ttl" default:"24h" desc:"a user session's TTL"`
ClaimScopes map[string]string `envconfig:"claim_scopes" default:"name:profile,family_name:profile,given_name:profile,email:email,https%3A%2F%2Fgithub.com%2Fi-core%2Fwerther%2Fclaims%2Froles:roles" desc:"a mapping of OpenID Connect claims to scopes (all claims are URL encoded)"`
HydraURL string `envconfig:"hydra_url" required:"true" desc:"an admin URL of ORY Hydra Server"`
SessionTTL time.Duration `envconfig:"session_ttl" default:"24h" desc:"a user session's TTL"`
ClaimScopes map[string]string `envconfig:"claim_scopes" default:"name:profile,family_name:profile,given_name:profile,email:email,https%3A%2F%2Fgithub.com%2Fi-core%2Fwerther%2Fclaims%2Froles:roles" desc:"a mapping of OpenID Connect claims to scopes (all claims are URL encoded)"`
FakeTLSTermination bool `envconfig:"fake_tls_termination" default:"false" desc:"Fake tls termination by adding \"X-Forwarded-Proto: https\" to http headers "`
ACR string `envconfig:"acr" desc:"Authorization Context Class Reference"`
AMR []string `envconfig:"amr" desc:"Authentication Method References"`
}
// UserManager is an interface that is used for authentication and providing user's claims.
@ -83,10 +87,10 @@ func NewHandler(cnf Config, um UserManager, tr TemplateRenderer) *Handler {
// AddRoutes registers all required routes for Login & Consent Provider.
func (h *Handler) AddRoutes(apply func(m, p string, h http.Handler, mws ...func(http.Handler) http.Handler)) {
sessionTTL := int(h.SessionTTL.Seconds())
apply(http.MethodGet, "/login", newLoginStartHandler(hydra.NewLoginReqDoer(h.HydraURL, 0), h.tr))
apply(http.MethodPost, "/login", newLoginEndHandler(hydra.NewLoginReqDoer(h.HydraURL, sessionTTL), h.um, h.tr))
apply(http.MethodGet, "/consent", newConsentHandler(hydra.NewConsentReqDoer(h.HydraURL, sessionTTL), h.um, h.ClaimScopes))
apply(http.MethodGet, "/logout", newLogoutHandler(hydra.NewLogoutReqDoer(h.HydraURL)))
apply(http.MethodGet, "/login", newLoginStartHandler(hydra.NewLoginReqDoer(h.HydraURL, h.FakeTLSTermination, 0, h.ACR, h.AMR), h.tr))
apply(http.MethodPost, "/login", newLoginEndHandler(hydra.NewLoginReqDoer(h.HydraURL, h.FakeTLSTermination, sessionTTL, h.ACR, h.AMR), h.um, h.tr))
apply(http.MethodGet, "/consent", newConsentHandler(hydra.NewConsentReqDoer(h.HydraURL, h.FakeTLSTermination, sessionTTL), h.um, h.ClaimScopes))
apply(http.MethodGet, "/logout", newLogoutHandler(hydra.NewLogoutReqDoer(h.HydraURL, h.FakeTLSTermination)))
}
// oa2LoginReqAcceptor is an interface that is used for accepting an OAuth2 login request.
@ -126,7 +130,8 @@ func newLoginStartHandler(rproc oa2LoginReqProcessor, tmplRenderer TemplateRende
return
}
log.Infow("Failed to initiate an OAuth2 login request", zap.Error(err), "challenge", challenge)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
errMsg := fmt.Sprintf("%s - %s - %s", http.StatusText(http.StatusInternalServerError), err, errors.Cause(err))
http.Error(w, errMsg, http.StatusInternalServerError)
return
}
log.Infow("A login request is initiated", "challenge", challenge, "username", ri.Subject)
@ -170,7 +175,7 @@ func newLoginEndHandler(ra oa2LoginReqAcceptor, auther authenticator, tmplRender
data := LoginTmplData{
CSRFToken: nosurf.Token(r),
Challenge: challenge,
LoginURL: r.URL.String(),
LoginURL: strings.TrimPrefix(r.URL.String(), "/"),
}
username, password := r.Form.Get("username"), r.Form.Get("password")

View File

@ -48,17 +48,20 @@ type connector interface {
// Config is a LDAP configuration.
type Config struct {
Endpoints []string `envconfig:"endpoints" required:"true" desc:"a LDAP's server URLs as \"<address>:<port>\""`
BindDN string `envconfig:"binddn" desc:"a LDAP bind DN"`
BindPass string `envconfig:"bindpw" json:"-" desc:"a LDAP bind password"`
BaseDN string `envconfig:"basedn" required:"true" desc:"a LDAP base DN for searching users"`
AttrClaims map[string]string `envconfig:"attr_claims" default:"name:name,sn:family_name,givenName:given_name,mail:email" desc:"a mapping of LDAP attributes to OpenID connect claims"`
RoleBaseDN string `envconfig:"role_basedn" required:"true" desc:"a LDAP base DN for searching roles"`
RoleAttr string `envconfig:"role_attr" default:"description" desc:"a LDAP group's attribute that contains a role's name"`
RoleClaim string `envconfig:"role_claim" default:"https://github.com/i-core/werther/claims/roles" desc:"a name of an OpenID Connect claim that contains user roles"`
CacheSize int `envconfig:"cache_size" default:"512" desc:"a user info cache's size in KiB"`
CacheTTL time.Duration `envconfig:"cache_ttl" default:"30m" desc:"a user info cache TTL"`
IsTLS bool `envconfig:"is_tls" default:"false" desc:"should LDAP connection be established via TLS"`
Endpoints []string `envconfig:"endpoints" required:"true" desc:"a LDAP's server URLs as \"<address>:<port>\""`
BindDN string `envconfig:"binddn" desc:"a LDAP bind DN"`
BindPass string `envconfig:"bindpw" json:"-" desc:"a LDAP bind password"`
BaseDN string `envconfig:"basedn" required:"true" desc:"a LDAP base DN for searching users"`
UserSearchQuery string `envconfig:"user_search_query" desc:"the user search query" default:"(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))"`
AttrClaims map[string]string `envconfig:"attr_claims" default:"name:name,sn:family_name,givenName:given_name,mail:email" desc:"a mapping of LDAP attributes to OpenID connect claims"`
RoleBaseDN string `envconfig:"role_basedn" required:"true" desc:"a LDAP base DN for searching roles"`
RoleSearchQuery string `envconfig:"role_search_query" desc:"the role search query" default:"(|(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s)))"`
RoleAttr string `envconfig:"role_attr" default:"description" desc:"a LDAP group's attribute that contains a role's name"`
RoleClaim string `envconfig:"role_claim" default:"https://github.com/i-core/werther/claims/roles" desc:"a name of an OpenID Connect claim that contains user roles"`
CacheSize int `envconfig:"cache_size" default:"512" desc:"a user info cache's size in KiB"`
CacheTTL time.Duration `envconfig:"cache_ttl" default:"30m" desc:"a user info cache TTL"`
IsTLS bool `envconfig:"is_tls" default:"false" desc:"should LDAP connection be established via TLS"`
ConnectionTimeout time.Duration `envconfig:"connection_timeout" default:"60s" desc:"LDAP server connection timeout"`
}
// Client is a LDAP client (compatible with Active Directory).
@ -71,9 +74,16 @@ type Client struct {
// New creates a new LDAP client.
func New(cnf Config) *Client {
return &Client{
Config: cnf,
connector: &ldapConnector{BaseDN: cnf.BaseDN, RoleBaseDN: cnf.RoleBaseDN, IsTLS: cnf.IsTLS},
cache: freecache.NewCache(cnf.CacheSize * 1024),
Config: cnf,
connector: &ldapConnector{
BaseDN: cnf.BaseDN,
UserSearchQuery: cnf.UserSearchQuery,
RoleBaseDN: cnf.RoleBaseDN,
IsTLS: cnf.IsTLS,
RoleSearchQuery: cnf.RoleSearchQuery,
ConnectionTimeout: cnf.ConnectionTimeout,
},
cache: freecache.NewCache(cnf.CacheSize * 1024),
}
}
@ -185,7 +195,7 @@ func (cli *Client) FindOIDCClaims(ctx context.Context, username string) (map[str
return nil, err
}
roles := make(map[string]interface{})
roles := make([]map[string]interface{}, 0)
for _, entry := range entries {
roleDN, ok := entry["dn"].(string)
if !ok || roleDN == "" {
@ -203,21 +213,8 @@ func (cli *Client) FindOIDCClaims(ctx context.Context, username string) (map[str
if n < k || !strings.EqualFold(roleDN[n-k:], cli.RoleBaseDN) {
panic("You should never see that")
}
// The DN without the role's base DN must contain a CN and OU
// where the CN is for uniqueness only, and the OU is an application id.
path := strings.Split(roleDN[:n-k-1], ",")
if len(path) != 2 {
log.Infow("A role's DN without the role's base DN must contain two nodes only",
"roleBaseDN", cli.RoleBaseDN, "roleDN", roleDN)
continue
}
appID := path[1][len("OU="):]
var appRoles []interface{}
if v := roles[appID]; v != nil {
appRoles = v.([]interface{})
}
roles[appID] = append(appRoles, entry[cli.RoleAttr])
roles = append(roles, entry)
}
claims[cli.RoleClaim] = roles
@ -296,13 +293,16 @@ func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string
}
type ldapConnector struct {
BaseDN string
RoleBaseDN string
IsTLS bool
BaseDN string
RoleBaseDN string
IsTLS bool
UserSearchQuery string
RoleSearchQuery string
ConnectionTimeout time.Duration
}
func (c *ldapConnector) Connect(ctx context.Context, addr string) (conn, error) {
d := net.Dialer{Timeout: ldap.DefaultTimeout}
d := net.Dialer{Timeout: c.ConnectionTimeout}
tcpcn, err := d.DialContext(ctx, "tcp", addr)
if err != nil {
return nil, err
@ -319,13 +319,21 @@ func (c *ldapConnector) Connect(ctx context.Context, addr string) (conn, error)
ldapcn := ldap.NewConn(tcpcn, c.IsTLS)
ldapcn.Start()
return &ldapConn{Conn: ldapcn, BaseDN: c.BaseDN, RoleBaseDN: c.RoleBaseDN}, nil
return &ldapConn{
Conn: ldapcn,
BaseDN: c.BaseDN,
UserSearchQuery: c.UserSearchQuery,
RoleBaseDN: c.RoleBaseDN,
RoleSearchQuery: c.RoleSearchQuery,
}, nil
}
type ldapConn struct {
*ldap.Conn
BaseDN string
RoleBaseDN string
BaseDN string
RoleBaseDN string
UserSearchQuery string
RoleSearchQuery string
}
func (c *ldapConn) Bind(bindDN, password string) error {
@ -337,17 +345,12 @@ func (c *ldapConn) Bind(bindDN, password string) error {
}
func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) {
query := fmt.Sprintf(
"(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))"+
"(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))", user)
query := fmt.Sprintf(c.UserSearchQuery, user)
return c.searchEntries(c.BaseDN, query, attrs)
}
func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) {
query := fmt.Sprintf("(|"+
"(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))"+
"(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s))"+
")", user)
query := fmt.Sprintf(c.RoleSearchQuery, user)
return c.searchEntries(c.RoleBaseDN, query, attrs)
}

View File

@ -89,7 +89,7 @@ func loginTmpl() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "login.tmpl", size: 1376, mode: os.FileMode(0664), modTime: time.Unix(1598432745, 0)}
info := bindataFileInfo{name: "login.tmpl", size: 1376, mode: os.FileMode(0644), modTime: time.Unix(1632751659, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x96, 0x82, 0x4c, 0x6a, 0xc3, 0x92, 0x44, 0x14, 0x82, 0xe7, 0x9a, 0xa8, 0xc8, 0x81, 0x35, 0x91, 0x53, 0xa8, 0x9, 0xe5, 0x8, 0xd5, 0xf, 0x5c, 0x48, 0x31, 0xde, 0xbf, 0xb7, 0x65, 0x23, 0xa9}}
return a, nil
}
@ -109,7 +109,7 @@ func staticFontsRobotoLightTtf() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.ttf", size: 170012, mode: os.FileMode(0644), modTime: time.Unix(1574945279, 0)}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.ttf", size: 170012, mode: os.FileMode(0644), modTime: time.Unix(1631861563, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdb, 0x2, 0x9, 0x6a, 0x91, 0xc2, 0xa, 0xb6, 0x2d, 0x45, 0x90, 0x1, 0xa1, 0x5, 0x9b, 0xc8, 0xd7, 0x8c, 0xaa, 0x35, 0xd6, 0x37, 0xdc, 0x91, 0x49, 0x4c, 0x44, 0x40, 0x81, 0x5a, 0x6a, 0xc1}}
return a, nil
}
@ -129,7 +129,7 @@ func staticFontsRobotoLightWoff() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.woff", size: 93468, mode: os.FileMode(0644), modTime: time.Unix(1574945279, 0)}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.woff", size: 93468, mode: os.FileMode(0644), modTime: time.Unix(1631861563, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0xad, 0xe0, 0x48, 0xda, 0x36, 0xf1, 0x3, 0xa5, 0x20, 0x45, 0x5a, 0xcb, 0xd2, 0xf, 0x26, 0xbd, 0x45, 0xd6, 0xf1, 0xc4, 0x21, 0x5, 0x4a, 0x5d, 0x92, 0x6f, 0x55, 0x65, 0x25, 0x16, 0x35}}
return a, nil
}
@ -149,7 +149,7 @@ func staticFontsRobotoLightWoff2() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.woff2", size: 64272, mode: os.FileMode(0644), modTime: time.Unix(1574945279, 0)}
info := bindataFileInfo{name: "static/fonts/Roboto-Light.woff2", size: 64272, mode: os.FileMode(0644), modTime: time.Unix(1631861563, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1a, 0x87, 0xc0, 0x97, 0xd1, 0xa3, 0x42, 0xec, 0x1b, 0x1a, 0x40, 0x6, 0x6d, 0x4d, 0xbe, 0x9d, 0x6f, 0x14, 0x49, 0x2d, 0x78, 0x80, 0x5d, 0xe2, 0xa1, 0xb5, 0x7d, 0xaf, 0x8b, 0x28, 0xd6, 0x40}}
return a, nil
}
@ -169,7 +169,7 @@ func staticScriptJs() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/script.js", size: 1240, mode: os.FileMode(0644), modTime: time.Unix(1565090829, 0)}
info := bindataFileInfo{name: "static/script.js", size: 1240, mode: os.FileMode(0644), modTime: time.Unix(1631861563, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0x83, 0x40, 0xc4, 0xb1, 0x4e, 0x2c, 0xf8, 0x84, 0x11, 0x9b, 0x80, 0xc2, 0xe6, 0xab, 0xb5, 0xf8, 0xd5, 0x3b, 0xc9, 0x2e, 0x5b, 0x12, 0x7, 0x29, 0x2f, 0x21, 0x5f, 0x59, 0x35, 0xf7, 0xad}}
return a, nil
}
@ -189,7 +189,7 @@ func staticStyleCss() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "static/style.css", size: 5966, mode: os.FileMode(0644), modTime: time.Unix(1574945279, 0)}
info := bindataFileInfo{name: "static/style.css", size: 5966, mode: os.FileMode(0644), modTime: time.Unix(1631861563, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdf, 0x10, 0x89, 0x7e, 0x7, 0xd2, 0xf2, 0xcc, 0xa2, 0x4e, 0xcf, 0x1, 0x63, 0x75, 0x97, 0xa1, 0x1c, 0x36, 0x4e, 0x34, 0x44, 0x85, 0x53, 0x93, 0xd4, 0x40, 0x69, 0x5f, 0x78, 0x30, 0x17, 0x8f}}
return a, nil
}

22
misc/packaging/nfpm.yml Normal file
View File

@ -0,0 +1,22 @@
name: "hydra-werther"
arch: "amd64"
platform: "linux"
version: "${PACKAGE_VERSION}"
section: "default"
priority: "extra"
maintainer: "Cadoles <contact@cadoles.com>"
description: |
PostgreSQL automated backup scripts
vendor: "Cadoles"
homepage: "https://forge.cadoles.com/Cadoles/postgres-backup"
license: "AGPL-3.0"
version_schema: none
contents:
- src: bin/werther_linux_amd64
dst: /usr/bin/hydra-werther
- src: conf/hydra-werther.conf
dst: /etc/hydra-werther/hydra-werther.conf
- src: misc/packaging/systemd/hydra-werther.service
dst: /usr/lib/systemd/system/hydra-werther.service

View File

@ -0,0 +1,12 @@
[Unit]
Description=Run Hydra Werther login/consent/logout app
After=network-online.target
[Service]
Type=simple
EnvironmentFile=/etc/hydra-werther/hydra-werther.conf
ExecStart=/usr/bin/hydra-werther
Restart=on-failure
[Install]
WantedBy=multi-user.target

21
misc/script/build Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
set -e
DISTS=${DISTS:-linux/386 linux/amd64 windows/amd64 darwin/amd64}
for dist in $DISTS
do
os=`echo $dist | cut -d'/' -f1`
arch=`echo $dist | cut -d'/' -f2`
env GOOS=$os GOARCH=$arch go build -o bin/werther_${os}_${arch} -ldflags "-w -s -X main.version=$(git describe --tags)" ./cmd/werther
if [[ "$os" = "windows" ]]; then
zip -r bin/werther_${os}_${arch}.zip bin/werther_${os}_${arch}
else
tar cvzf bin/werther_${os}_${arch}.tar.gz bin/werther_${os}_${arch}
fi
done
(cd bin && sha256sum *.{tar.gz,zip} > werther_checksums.txt || exit 0)