Compare commits
18 Commits
v1.2.0
...
1.2.1-2-4-
Author | SHA1 | Date | |
---|---|---|---|
592749eebf | |||
24b66a12ef | |||
194c1864c4 | |||
b940aae071 | |||
eab0b72431 | |||
3525b4bcb5 | |||
138e818429 | |||
c7599a8faa | |||
bd2c94fc15 | |||
fb981c5df9 | |||
2ad1595a34 | |||
938d1939d4 | |||
5ea0476107 | |||
cd8a983bdd | |||
6a4ab470b4 | |||
67c63ca8cd | |||
9f8461f71a | |||
91691f2a6a |
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/bin
|
||||||
|
/dist
|
@ -56,7 +56,7 @@ before_deploy:
|
|||||||
deploy:
|
deploy:
|
||||||
- provider: releases
|
- provider: releases
|
||||||
api_key:
|
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:
|
file:
|
||||||
- bin/werther_linux_386.tar.gz
|
- bin/werther_linux_386.tar.gz
|
||||||
- bin/werther_linux_amd64.tar.gz
|
- bin/werther_linux_amd64.tar.gz
|
||||||
|
50
Jenkinsfile
vendored
Normal file
50
Jenkinsfile
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
@Library('cadoles') _
|
||||||
|
|
||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
label 'docker'
|
||||||
|
filename 'Dockerfile'
|
||||||
|
dir 'misc/ci'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Build and publish packages') {
|
||||||
|
when {
|
||||||
|
anyOf {
|
||||||
|
branch 'master'
|
||||||
|
branch 'develop'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
List<String> packagers = ['deb', 'rpm']
|
||||||
|
packagers.each { pkgr ->
|
||||||
|
sh "make NFPM_PACKAGER='${pkgr}' build package"
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> attachments = sh(returnStdout: true, script: "find dist -type f -name '*.deb' -or -name '*.rpm' -or -name '*.ipk'").split(' ')
|
||||||
|
String releaseVersion = sh(returnStdout: true, script: "git describe --always | rev | cut -d '/' -f 1 | rev").trim()
|
||||||
|
|
||||||
|
String releaseBody = """
|
||||||
|
_Publication automatisée réalisée par Jenkins._ [Voir le job](${env.RUN_DISPLAY_URL})
|
||||||
|
"""
|
||||||
|
|
||||||
|
gitea.release('forge-jenkins', 'Cadoles', 'hydra-werther', [
|
||||||
|
'attachments': attachments,
|
||||||
|
'body': releaseBody,
|
||||||
|
'releaseName': "${releaseVersion}",
|
||||||
|
'releaseVersion': "${releaseVersion}"
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
always {
|
||||||
|
cleanWs()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
Makefile
Normal file
23
Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
PACKAGE_VERSION ?= $(shell git describe --always | rev | cut -d '/' -f 1 | rev)
|
||||||
|
NFPM_PACKAGER ?= deb
|
||||||
|
|
||||||
|
build: clean generate
|
||||||
|
CGO_ENABLED=0 misc/script/build
|
||||||
|
|
||||||
|
generate:
|
||||||
|
go generate ./...
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf bin
|
||||||
|
|
||||||
|
package: dist
|
||||||
|
PACKAGE_VERSION=$(PACKAGE_VERSION) \
|
||||||
|
nfpm package \
|
||||||
|
--config misc/packaging/nfpm.yml \
|
||||||
|
--target ./dist \
|
||||||
|
--packager $(NFPM_PACKAGER)
|
||||||
|
|
||||||
|
dist:
|
||||||
|
mkdir -p dist
|
||||||
|
|
||||||
|
.PHONY: build
|
125
conf/hydra-werther.conf
Normal file
125
conf/hydra-werther.conf
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
#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]
|
1
debian/compat
vendored
Normal file
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
9
|
14
debian/control
vendored
Normal file
14
debian/control
vendored
Normal 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
12
debian/hydra-werther.service
vendored
Normal 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
40
debian/rules
vendored
Normal 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
1
debian/source/format
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.0 (native)
|
@ -13,18 +13,19 @@ import (
|
|||||||
|
|
||||||
// ConsentReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
|
// ConsentReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
|
||||||
type ConsentReqDoer struct {
|
type ConsentReqDoer struct {
|
||||||
hydraURL string
|
hydraURL string
|
||||||
rememberFor int
|
fakeTLSTermination bool
|
||||||
|
rememberFor int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConsentReqDoer creates a ConsentRequest.
|
// NewConsentReqDoer creates a ConsentRequest.
|
||||||
func NewConsentReqDoer(hydraURL string, rememberFor int) *ConsentReqDoer {
|
func NewConsentReqDoer(hydraURL string, fakeTLSTermination bool, rememberFor int) *ConsentReqDoer {
|
||||||
return &ConsentReqDoer{hydraURL: hydraURL, rememberFor: rememberFor}
|
return &ConsentReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination, rememberFor: rememberFor}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitiateRequest fetches information on the OAuth2 request.
|
// InitiateRequest fetches information on the OAuth2 request.
|
||||||
func (crd *ConsentReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
|
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")
|
return ri, errors.Wrap(err, "failed to initiate consent request")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,6 +47,6 @@ func (crd *ConsentReqDoer) AcceptConsentRequest(challenge string, remember bool,
|
|||||||
IDToken: idToken,
|
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")
|
return redirectURI, errors.Wrap(err, "failed to accept consent request")
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ func TestInitiateConsentRequest(t *testing.T) {
|
|||||||
h := &testInitiateConsentHandler{reqInfo: tc.reqInfo, status: tc.status}
|
h := &testInitiateConsentHandler{reqInfo: tc.reqInfo, status: tc.status}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewConsentReqDoer(srv.URL, tc.rememberFor)
|
ldr := hydra.NewConsentReqDoer(srv.URL, false, tc.rememberFor)
|
||||||
|
|
||||||
reqInfo, err := ldr.InitiateRequest(tc.challenge)
|
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}
|
h := &testAcceptConsentHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewConsentReqDoer(srv.URL, tc.rememberFor)
|
ldr := hydra.NewConsentReqDoer(srv.URL, false, tc.rememberFor)
|
||||||
|
|
||||||
var grantScope []string
|
var grantScope []string
|
||||||
for _, v := range tc.grantScope {
|
for _, v := range tc.grantScope {
|
||||||
|
@ -44,7 +44,7 @@ type ReqInfo struct {
|
|||||||
Subject string `json:"subject"`
|
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 == "" {
|
if challenge == "" {
|
||||||
return nil, ErrChallengeMissed
|
return nil, ErrChallengeMissed
|
||||||
}
|
}
|
||||||
@ -58,7 +58,16 @@ func initiateRequest(typ reqType, hydraURL, challenge string) (*ReqInfo, error)
|
|||||||
}
|
}
|
||||||
u = u.ResolveReference(ref)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -76,7 +85,7 @@ func initiateRequest(typ reqType, hydraURL, challenge string) (*ReqInfo, error)
|
|||||||
return &ri, nil
|
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 == "" {
|
if challenge == "" {
|
||||||
return "", ErrChallengeMissed
|
return "", ErrChallengeMissed
|
||||||
}
|
}
|
||||||
@ -101,6 +110,10 @@ func acceptRequest(typ reqType, hydraURL, challenge string, data interface{}) (s
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
if fakeTLSTermination {
|
||||||
|
r.Header.Add("X-Forwarded-Proto", "https")
|
||||||
|
}
|
||||||
|
|
||||||
r.Header.Set("Content-Type", "application/json")
|
r.Header.Set("Content-Type", "application/json")
|
||||||
resp, err := http.DefaultClient.Do(r)
|
resp, err := http.DefaultClient.Do(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -13,18 +13,19 @@ import (
|
|||||||
|
|
||||||
// LoginReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
|
// LoginReqDoer fetches information on the OAuth2 request and then accept or reject the requested authentication process.
|
||||||
type LoginReqDoer struct {
|
type LoginReqDoer struct {
|
||||||
hydraURL string
|
hydraURL string
|
||||||
rememberFor int
|
fakeTLSTermination bool
|
||||||
|
rememberFor int
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLoginReqDoer creates a LoginRequest.
|
// NewLoginReqDoer creates a LoginRequest.
|
||||||
func NewLoginReqDoer(hydraURL string, rememberFor int) *LoginReqDoer {
|
func NewLoginReqDoer(hydraURL string, fakeTLSTermination bool, rememberFor int) *LoginReqDoer {
|
||||||
return &LoginReqDoer{hydraURL: hydraURL, rememberFor: rememberFor}
|
return &LoginReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination, rememberFor: rememberFor}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitiateRequest fetches information on the OAuth2 request.
|
// InitiateRequest fetches information on the OAuth2 request.
|
||||||
func (lrd *LoginReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
|
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")
|
return ri, errors.Wrap(err, "failed to initiate login request")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,6 +40,6 @@ func (lrd *LoginReqDoer) AcceptLoginRequest(challenge string, remember bool, sub
|
|||||||
RememberFor: lrd.rememberFor,
|
RememberFor: lrd.rememberFor,
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
}
|
}
|
||||||
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")
|
return redirectURI, errors.Wrap(err, "failed to accept login request")
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ func TestInitiateLoginRequest(t *testing.T) {
|
|||||||
h := &testInitiateLoginHandler{reqInfo: tc.reqInfo, status: tc.status}
|
h := &testInitiateLoginHandler{reqInfo: tc.reqInfo, status: tc.status}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewLoginReqDoer(srv.URL, 0)
|
ldr := hydra.NewLoginReqDoer(srv.URL, false, 0)
|
||||||
|
|
||||||
reqInfo, err := ldr.InitiateRequest(tc.challenge)
|
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}
|
h := &testAcceptLoginHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewLoginReqDoer(srv.URL, tc.rememberFor)
|
ldr := hydra.NewLoginReqDoer(srv.URL, false, tc.rememberFor)
|
||||||
|
|
||||||
redirect, err := ldr.AcceptLoginRequest(tc.challenge, tc.remember, tc.subject)
|
redirect, err := ldr.AcceptLoginRequest(tc.challenge, tc.remember, tc.subject)
|
||||||
|
|
||||||
|
@ -13,22 +13,23 @@ import (
|
|||||||
|
|
||||||
// LogoutReqDoer fetches information on the OAuth2 request and then accepts or rejects the requested logout process.
|
// LogoutReqDoer fetches information on the OAuth2 request and then accepts or rejects the requested logout process.
|
||||||
type LogoutReqDoer struct {
|
type LogoutReqDoer struct {
|
||||||
hydraURL string
|
hydraURL string
|
||||||
|
fakeTLSTermination bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogoutReqDoer creates a LogoutRequest.
|
// NewLogoutReqDoer creates a LogoutRequest.
|
||||||
func NewLogoutReqDoer(hydraURL string) *LogoutReqDoer {
|
func NewLogoutReqDoer(hydraURL string, fakeTLSTermination bool) *LogoutReqDoer {
|
||||||
return &LogoutReqDoer{hydraURL: hydraURL}
|
return &LogoutReqDoer{hydraURL: hydraURL, fakeTLSTermination: fakeTLSTermination}
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitiateRequest fetches information on the OAuth2 request.
|
// InitiateRequest fetches information on the OAuth2 request.
|
||||||
func (lrd *LogoutReqDoer) InitiateRequest(challenge string) (*ReqInfo, error) {
|
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")
|
return ri, errors.Wrap(err, "failed to initiate logout request")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AcceptLogoutRequest accepts the requested logout process, and returns redirect URI.
|
// AcceptLogoutRequest accepts the requested logout process, and returns redirect URI.
|
||||||
func (lrd *LogoutReqDoer) AcceptLogoutRequest(challenge string) (string, error) {
|
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")
|
return redirectURI, errors.Wrap(err, "failed to accept logout request")
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,7 @@ func TestInitiateLogoutRequest(t *testing.T) {
|
|||||||
h := &testInitiateLogoutHandler{reqInfo: tc.reqInfo, status: tc.status}
|
h := &testInitiateLogoutHandler{reqInfo: tc.reqInfo, status: tc.status}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewLogoutReqDoer(srv.URL)
|
ldr := hydra.NewLogoutReqDoer(srv.URL, false)
|
||||||
|
|
||||||
reqInfo, err := ldr.InitiateRequest(tc.challenge)
|
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}
|
h := &testAcceptLogoutHandler{challenge: tc.challenge, status: tc.status, redirect: tc.redirect}
|
||||||
srv := httptest.NewServer(h)
|
srv := httptest.NewServer(h)
|
||||||
defer srv.Close()
|
defer srv.Close()
|
||||||
ldr := hydra.NewLogoutReqDoer(srv.URL)
|
ldr := hydra.NewLogoutReqDoer(srv.URL, false)
|
||||||
|
|
||||||
redirect, err := ldr.AcceptLogoutRequest(tc.challenge)
|
redirect, err := ldr.AcceptLogoutRequest(tc.challenge)
|
||||||
|
|
||||||
|
@ -27,9 +27,10 @@ const loginTmplName = "login.tmpl"
|
|||||||
|
|
||||||
// Config is a Hydra configuration.
|
// Config is a Hydra configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
HydraURL string `envconfig:"hydra_url" required:"true" desc:"an admin URL of ORY Hydra Server"`
|
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"`
|
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)"`
|
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 "`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserManager is an interface that is used for authentication and providing user's claims.
|
// UserManager is an interface that is used for authentication and providing user's claims.
|
||||||
@ -83,10 +84,10 @@ func NewHandler(cnf Config, um UserManager, tr TemplateRenderer) *Handler {
|
|||||||
// AddRoutes registers all required routes for Login & Consent Provider.
|
// 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)) {
|
func (h *Handler) AddRoutes(apply func(m, p string, h http.Handler, mws ...func(http.Handler) http.Handler)) {
|
||||||
sessionTTL := int(h.SessionTTL.Seconds())
|
sessionTTL := int(h.SessionTTL.Seconds())
|
||||||
apply(http.MethodGet, "/login", newLoginStartHandler(hydra.NewLoginReqDoer(h.HydraURL, 0), h.tr))
|
apply(http.MethodGet, "/login", newLoginStartHandler(hydra.NewLoginReqDoer(h.HydraURL, h.FakeTLSTermination, 0), h.tr))
|
||||||
apply(http.MethodPost, "/login", newLoginEndHandler(hydra.NewLoginReqDoer(h.HydraURL, sessionTTL), h.um, h.tr))
|
apply(http.MethodPost, "/login", newLoginEndHandler(hydra.NewLoginReqDoer(h.HydraURL, h.FakeTLSTermination, sessionTTL), h.um, h.tr))
|
||||||
apply(http.MethodGet, "/consent", newConsentHandler(hydra.NewConsentReqDoer(h.HydraURL, sessionTTL), h.um, h.ClaimScopes))
|
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)))
|
apply(http.MethodGet, "/logout", newLogoutHandler(hydra.NewLogoutReqDoer(h.HydraURL, h.FakeTLSTermination)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// oa2LoginReqAcceptor is an interface that is used for accepting an OAuth2 login request.
|
// oa2LoginReqAcceptor is an interface that is used for accepting an OAuth2 login request.
|
||||||
@ -170,7 +171,7 @@ func newLoginEndHandler(ra oa2LoginReqAcceptor, auther authenticator, tmplRender
|
|||||||
data := LoginTmplData{
|
data := LoginTmplData{
|
||||||
CSRFToken: nosurf.Token(r),
|
CSRFToken: nosurf.Token(r),
|
||||||
Challenge: challenge,
|
Challenge: challenge,
|
||||||
LoginURL: r.URL.String(),
|
LoginURL: strings.TrimPrefix(r.URL.String(), "/"),
|
||||||
}
|
}
|
||||||
|
|
||||||
username, password := r.Form.Get("username"), r.Form.Get("password")
|
username, password := r.Form.Get("username"), r.Form.Get("password")
|
||||||
|
@ -48,17 +48,20 @@ type connector interface {
|
|||||||
|
|
||||||
// Config is a LDAP configuration.
|
// Config is a LDAP configuration.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Endpoints []string `envconfig:"endpoints" required:"true" desc:"a LDAP's server URLs as \"<address>:<port>\""`
|
Endpoints []string `envconfig:"endpoints" required:"true" desc:"a LDAP's server URLs as \"<address>:<port>\""`
|
||||||
BindDN string `envconfig:"binddn" desc:"a LDAP bind DN"`
|
BindDN string `envconfig:"binddn" desc:"a LDAP bind DN"`
|
||||||
BindPass string `envconfig:"bindpw" json:"-" desc:"a LDAP bind password"`
|
BindPass string `envconfig:"bindpw" json:"-" desc:"a LDAP bind password"`
|
||||||
BaseDN string `envconfig:"basedn" required:"true" desc:"a LDAP base DN for searching users"`
|
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"`
|
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)))"`
|
||||||
RoleBaseDN string `envconfig:"role_basedn" required:"true" desc:"a LDAP base DN for searching roles"`
|
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"`
|
||||||
RoleAttr string `envconfig:"role_attr" default:"description" desc:"a LDAP group's attribute that contains a role's name"`
|
RoleBaseDN string `envconfig:"role_basedn" required:"true" desc:"a LDAP base DN for searching roles"`
|
||||||
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"`
|
RoleSearchQuery string `envconfig:"role_search_query" desc:"the role search query" default:"(|(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s)))"`
|
||||||
CacheSize int `envconfig:"cache_size" default:"512" desc:"a user info cache's size in KiB"`
|
RoleAttr string `envconfig:"role_attr" default:"description" desc:"a LDAP group's attribute that contains a role's name"`
|
||||||
CacheTTL time.Duration `envconfig:"cache_ttl" default:"30m" desc:"a user info cache TTL"`
|
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"`
|
||||||
IsTLS bool `envconfig:"is_tls" default:"false" desc:"should LDAP connection be established via TLS"`
|
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).
|
// Client is a LDAP client (compatible with Active Directory).
|
||||||
@ -71,9 +74,16 @@ type Client struct {
|
|||||||
// New creates a new LDAP client.
|
// New creates a new LDAP client.
|
||||||
func New(cnf Config) *Client {
|
func New(cnf Config) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
Config: cnf,
|
Config: cnf,
|
||||||
connector: &ldapConnector{BaseDN: cnf.BaseDN, RoleBaseDN: cnf.RoleBaseDN, IsTLS: cnf.IsTLS},
|
connector: &ldapConnector{
|
||||||
cache: freecache.NewCache(cnf.CacheSize * 1024),
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
roles := make(map[string]interface{})
|
roles := make([]map[string]interface{}, 0)
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
roleDN, ok := entry["dn"].(string)
|
roleDN, ok := entry["dn"].(string)
|
||||||
if !ok || roleDN == "" {
|
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) {
|
if n < k || !strings.EqualFold(roleDN[n-k:], cli.RoleBaseDN) {
|
||||||
panic("You should never see that")
|
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{}
|
roles = append(roles, entry)
|
||||||
if v := roles[appID]; v != nil {
|
|
||||||
appRoles = v.([]interface{})
|
|
||||||
}
|
|
||||||
roles[appID] = append(appRoles, entry[cli.RoleAttr])
|
|
||||||
}
|
}
|
||||||
claims[cli.RoleClaim] = roles
|
claims[cli.RoleClaim] = roles
|
||||||
|
|
||||||
@ -296,13 +293,16 @@ func (cli *Client) findBasicUserDetails(cn conn, username string, attrs []string
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ldapConnector struct {
|
type ldapConnector struct {
|
||||||
BaseDN string
|
BaseDN string
|
||||||
RoleBaseDN string
|
RoleBaseDN string
|
||||||
IsTLS bool
|
IsTLS bool
|
||||||
|
UserSearchQuery string
|
||||||
|
RoleSearchQuery string
|
||||||
|
ConnectionTimeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ldapConnector) Connect(ctx context.Context, addr string) (conn, error) {
|
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)
|
tcpcn, err := d.DialContext(ctx, "tcp", addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 := ldap.NewConn(tcpcn, c.IsTLS)
|
||||||
|
|
||||||
ldapcn.Start()
|
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 {
|
type ldapConn struct {
|
||||||
*ldap.Conn
|
*ldap.Conn
|
||||||
BaseDN string
|
BaseDN string
|
||||||
RoleBaseDN string
|
RoleBaseDN string
|
||||||
|
UserSearchQuery string
|
||||||
|
RoleSearchQuery string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ldapConn) Bind(bindDN, password string) error {
|
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) {
|
func (c *ldapConn) SearchUser(user string, attrs ...string) ([]map[string]interface{}, error) {
|
||||||
query := fmt.Sprintf(
|
query := fmt.Sprintf(c.UserSearchQuery, user)
|
||||||
"(&(|(objectClass=organizationalPerson)(objectClass=inetOrgPerson))"+
|
|
||||||
"(|(uid=%[1]s)(mail=%[1]s)(userPrincipalName=%[1]s)(sAMAccountName=%[1]s)))", user)
|
|
||||||
return c.searchEntries(c.BaseDN, query, attrs)
|
return c.searchEntries(c.BaseDN, query, attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) {
|
func (c *ldapConn) SearchUserRoles(user string, attrs ...string) ([]map[string]interface{}, error) {
|
||||||
query := fmt.Sprintf("(|"+
|
query := fmt.Sprintf(c.RoleSearchQuery, user)
|
||||||
"(&(|(objectClass=group)(objectClass=groupOfNames))(member=%[1]s))"+
|
|
||||||
"(&(objectClass=groupOfUniqueNames)(uniqueMember=%[1]s))"+
|
|
||||||
")", user)
|
|
||||||
return c.searchEntries(c.RoleBaseDN, query, attrs)
|
return c.searchEntries(c.RoleBaseDN, query, attrs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
misc/ci/Dockerfile
Normal file
9
misc/ci/Dockerfile
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
FROM alpine:3.16
|
||||||
|
|
||||||
|
RUN apk add --no-cache make git curl jq bash openssl go zip
|
||||||
|
|
||||||
|
RUN curl -k https://forge.cadoles.com/Cadoles/Jenkins/raw/branch/master/resources/com/cadoles/common/add-letsencrypt-ca.sh | bash
|
||||||
|
|
||||||
|
RUN wget https://github.com/goreleaser/nfpm/releases/download/v2.20.0/nfpm_2.20.0_Linux_x86_64.tar.gz \
|
||||||
|
&& tar -xzf nfpm_2.20.0_Linux_x86_64.tar.gz -C /usr/local/bin \
|
||||||
|
&& chmod +x /usr/local/bin/nfpm
|
21
misc/packaging/nfpm.yml
Normal file
21
misc/packaging/nfpm.yml
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
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"
|
||||||
|
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
|
||||||
|
|
||||||
|
|
12
misc/packaging/systemd/hydra-werther.service
Normal file
12
misc/packaging/systemd/hydra-werther.service
Normal 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
21
misc/script/build
Executable 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)
|
Reference in New Issue
Block a user