ci/cd: add CI/CD via Travis CI

This commit is contained in:
Nikolay Stupak 2019-06-05 11:08:52 +03:00
parent 3bbac7bb74
commit 4372cf72f2
3 changed files with 256 additions and 95 deletions

83
.travis.yml Normal file
View File

@ -0,0 +1,83 @@
language: go
go:
- 1.12.x
services:
- docker
env:
global:
- CGO_ENABLED=0
- GO111MODULE=on
- GOPROXY=https://proxy.golang.org
cache:
directories:
- "$GOPATH/pkg/mod"
- "$GOPATH/bin"
install: "(cd $HOME && go get -v github.com/golangci/golangci-lint/cmd/golangci-lint@v1.16.0)"
script:
- go test -v -coverprofile=coverage.txt ./...
- golangci-lint -v run
- |
set -e
for dist in linux/386 linux/amd64 windows/amd64 darwin/amd64
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=$TRAVIS_TAG" ./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)
- |
set -e
docker build --build-arg GOPROXY --build-arg VERSION=$TRAVIS_TAG -t "icoreru/werther:$TRAVIS_COMMIT" .
if [ -n "$TRAVIS_TAG" ]; then
docker tag "icoreru/werther:$TRAVIS_COMMIT" "icoreru/werther:$TRAVIS_TAG"
docker tag "icoreru/werther:$TRAVIS_COMMIT" "icoreru/werther:latest"
fi
after_success:
- bash <(curl -s https://codecov.io/bash)
before_deploy:
- |
if [ -n "$TRAVIS_TAG" ]; then
docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD"
fi
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=
file:
- bin/werther_linux_386.tar.gz
- bin/werther_linux_amd64.tar.gz
- bin/werther_windows_amd64.zip
- bin/werther_darwin_amd64.tar.gz
- bin/werther_checksums.txt
skip_cleanup: true
on:
tags: true
condition: $TRAVIS_OS_NAME = linux
- provider: script
skip_cleanup: true
script: docker push "icoreru/werther:$TRAVIS_TAG"
on:
tags: true
condition: $TRAVIS_OS_NAME = linux
- provider: script
skip_cleanup: true
script: docker push "icoreru/werther:latest"
on:
tags: true
condition: $TRAVIS_OS_NAME = linux

244
README.md
View File

@ -1,6 +1,6 @@
# Werther <sup>[1](#myfootnote1)</sup> # Werther <sup>[1](#myfootnote1)</sup>
[![GoDoc][doc-img]][doc] [![Build Status][build-img]][build] [![codecov][codecov-img]][codecov] [![GoDoc][doc-img]][doc] [![Build Status][build-img]][build] [![codecov][codecov-img]][codecov] [![Go Report Card][goreport-img]][goreport]
Werther is an Identity Provider for [ORY Hydra][hydra] over [LDAP][ldap]. Werther is an Identity Provider for [ORY Hydra][hydra] over [LDAP][ldap].
It implements [Login And Consent Flow][hydra-login-consent] and provides basic UI. It implements [Login And Consent Flow][hydra-login-consent] and provides basic UI.
@ -30,10 +30,10 @@ ORY Hydra v1.0.0-rc.12 or higher.
- [Installing](#installing) - [Installing](#installing)
- [Usage](#usage)
- [Configuration](#configuration) - [Configuration](#configuration)
- [User roles](#user-roles) - [User roles](#user-roles)
- [UI customization](#ui-customization) - [UI customization](#ui-customization)
- [Example](#example)
- [Resources](#resources) - [Resources](#resources)
- [Footnotes](#footnotes) - [Footnotes](#footnotes)
- [Contributing](#contributing) - [Contributing](#contributing)
@ -46,7 +46,7 @@ ORY Hydra v1.0.0-rc.12 or higher.
### From Docker ### From Docker
```bash ```bash
docker pull icoreru/werter docker pull icoreru/werther
``` ```
### From sources ### From sources
@ -55,44 +55,6 @@ docker pull icoreru/werter
go install ./... go install ./...
``` ```
## Usage
1. Create a network:
```
docker network create hydra-net
```
2. Run ORY Hydra:
```
docker run --network hydra-net -d --restart always --name hydra \
-p 4444:4444 \
-p 4445:4445 \
-e URLS_SELF_ISSUER=http://localhost:4444 \
-e URLS_SELF_PUBLIC=http://localhost:4444 \
-e URLS_LOGIN=http://localhost:8080/auth/login \
-e URLS_CONSENT=http://localhost:8080/auth/consent \
-e URLS_LOGOUT=http://localhost:8080/auth/logout \
-e WEBFINGER_OIDC_DISCOVERY_SUPPORTED_SCOPES=profile,email,phone \
-e WEBFINGER_OIDC_DISCOVERY_SUPPORTED_CLAIMS=name,family_name,given_name,nickname,email,phone_number \
-e DSN=memory \
oryd/hydra:v1.0.0-rc.12 serve all
```
Look for details in [ORY Hydra Configuration][hydra-doc-config] and [ORY Hydra Documentation][hydra-doc].
3. Run Werther:
```
docker run --network hydra-net -d --restart always --name werther \
-p 8080:8080 \
-e WERTHER_IDENTP_HYDRA_URL=http://hydra:4445 \
-e WERTHER_LDAP_ENDPOINTS=icdc0.example.local:389,icdc1.example.local:389 \
-e WERTHER_LDAP_BINDDN=<BINDDN> \
-e WERTHER_LDAP_BINDPW=<BINDDN_PASSWORD> \
-e WERTHER_LDAP_BASEDN="DC=example,DC=local" \
-e WERTHER_LDAP_ROLE_BASEDN="OU=AppRoles,OU=Domain Groups,DC=example,DC=local" \
icoreru/werther
```
## Configuration ## Configuration
The application is configured via environment variables. The application is configured via environment variables.
@ -113,16 +75,16 @@ For example, create an OU that repserents an application, and then in the create
create groups that represent application's roles: create groups that represent application's roles:
``` ```
DC=local dc=com
|-- OU=Domain Groups |-- dc=example
|-- OU=AppRoles |-- ou=AppRoles
|-- OU=App1 |-- ou=App1
|-- CN=app1_role1 (objectClass="group", description="role1") |-- cn=app1_role1 (objectClass="group", description="role1")
|-- CN=app1_role2 (objectClass="group", description="role2") |-- cn=app1_role2 (objectClass="group", description="role2")
``` ```
Run Werther with the environment variable `WERTHER_LDAP_ROLE_DN` Run Werther with the environment variable `WERTHER_LDAP_ROLE_DN`
that equals to `OU=AppRoles,OU=Domain Groups,DC=local`. that equals to `ou=AppRoles,dc=example,dc=com`.
In the above example Werther returns user's roles as a value In the above example Werther returns user's roles as a value
of the user role's claim `https://github.com/i-core/werther/claims/roles`. of the user role's claim `https://github.com/i-core/werther/claims/roles`.
@ -142,23 +104,23 @@ For more details about claims naming see [OpenID Connect Core 1.0][oidc-spec-add
For example, when we want to configure multiple applications or several environments for the same application. For example, when we want to configure multiple applications or several environments for the same application.
``` ```
DC=local dc=com
|-- OU=Domain Groups |-- dc=example
|-- OU=AppRoles |-- ou=AppRoles
|-- OU=Test |-- ou=Test
|-- OU=App1 |-- ou=App1
|-- CN=test_app1_role1 (objectClass="group", description="role1") |-- cn=test_app1_role1 (objectClass="group", description="role1")
|-- CN=test_app1_role2 (objectClass="group", description="role2") |-- cn=test_app1_role2 (objectClass="group", description="role2")
|-- OU=App2 |-- ou=App2
|-- CN=test_app2_role1 (objectClass="group",description-"role1") |-- cn=test_app2_role1 (objectClass="group",description-"role1")
|-- CN=test_app2_role2 (objectClass="group",description-"role2") |-- cn=test_app2_role2 (objectClass="group",description-"role2")
|-- OU=Dev |-- ou=Dev
|-- OU=App1 |-- ou=App1
|-- CN=dev_app1_role1 (objectClass="group", description="role1") |-- cn=dev_app1_role1 (objectClass="group", description="role1")
|-- CN=dev_app1_role3 (objectClass="group", description="role3") |-- cn=dev_app1_role3 (objectClass="group", description="role3")
|-- OU=App2 |-- ou=App2
|-- CN=dev_app2_role1 (objectClass="group",description-"role1") |-- cn=dev_app2_role1 (objectClass="group",description-"role1")
|-- CN=dev_app2_role4 (objectClass="group",description-"role4") |-- cn=dev_app2_role4 (objectClass="group",description-"role4")
``` ```
Active Directory requires unique CNs in a domain. But in Active Directory Active Directory requires unique CNs in a domain. But in Active Directory
@ -168,7 +130,7 @@ A name of a LDAP attribute is specified using the environment variable `WERTHER_
and has the default value `description`. and has the default value `description`.
In the above example, Werther returns a response that contains the next roles: In the above example, Werther returns a response that contains the next roles:
* when the environment variable `WERTHER_LDAP_ROLE_DN` equals to `OU=Test,OU=AppRoles,OU=Domain Groups,DC=local`: * when the environment variable `WERTHER_LDAP_ROLE_DN` equals to `ou=Test,ou=AppRoles,dc=example,dc=com`:
```json ```json
{ {
"https://github.com/i-core/werther/claims/roles": { "https://github.com/i-core/werther/claims/roles": {
@ -177,7 +139,7 @@ In the above example, Werther returns a response that contains the next roles:
} }
} }
``` ```
* when the environment variable `WERTHER_LDAP_ROLE_DN` equals to `OU=Dev,OU=AppRoles,OU=Domain Groups,DC=local`: * when the environment variable `WERTHER_LDAP_ROLE_DN` equals to `ou=Dev,ou=AppRoles,dc=example,dc=com`:
```json ```json
{ {
"https://github.com/i-core/werther/claims/roles": { "https://github.com/i-core/werther/claims/roles": {
@ -191,21 +153,7 @@ In the above example, Werther returns a response that contains the next roles:
Werther uses the Go templates to render UI pages. Werther uses the Go templates to render UI pages.
To customize the UI you should create a directory that contains UI pages' templates. To customize the UI you should create a directory that contains UI pages' templates.
After that you should set the directory path to the environment variable `WERTHER_WEB_DIR`: After that you should set the directory path to the environment variable `WERTHER_WEB_DIR`.
```bash
docker run --network hydra-net -d --restart always --name werther \
-p 8080:8080 \
-v /opt/werther/web:/path/to/custom-login-page/dir \
-e WERTHER_IDENTP_HYDRA_URL=http://hydra:4445 \
-e WERTHER_LDAP_ENDPOINTS=icdc0.example.local:389,icdc1.example.local:389 \
-e WERTHER_LDAP_BINDDN=<BINDDN> \
-e WERTHER_LDAP_BINDPW=<BINDDN_PASSWORD> \
-e WERTHER_LDAP_BASEDN="DC=example,DC=local" \
-e WERTHER_LDAP_ROLE_BASEDN="OU=AppRoles,OU=Domain Groups,DC=example,DC=local" \
-e WERTHER_WEB_DIR=/opt/werther/web
icoreru/werther
```
### Custom login page ### Custom login page
@ -222,11 +170,137 @@ they must be placed in a subdirectory called `static`.
For a full example of a login page's template see [source code](internal/web/templates). For a full example of a login page's template see [source code](internal/web/templates).
## Example
1. Create file `ldap.ldif`:
```
dn: uid=kolya_gerasyimov,ou=Users,dc=example,dc=com
objectClass: inetOrgPerson
cn: Kolya Gerasyimov
sn: Gerasyimov
uid: kolya_gerasyimov
userPassword: 123
mail: kolya_gerasyimov@example.com
ou: Users
dn: ou=AppRoles,dc=example,dc=com
objectClass: organizationalunit
ou: AppRoles
description: AppRoles
dn: ou=App1,ou=AppRoles,dc=example,dc=com
objectClass: organizationalunit
ou: App1
description: App1
dn: cn=traveler,ou=App1,ou=AppRoles,dc=example,dc=com
objectClass: groupofnames
cn: traveler
description: traveler
member: uid=kolya_gerasyimov,ou=Users,dc=example,dc=com
```
2. Create file `docker-compose.yml`:
```yaml
version: "3"
services:
hydra-client:
image: oryd/hydra:v1.0.0-rc.12
environment:
HYDRA_ADMIN_URL: http://hydra:4445
command:
- clients
- create
- --skip-tls-verify
- --id
- test-client
- --secret
- test-secret
- --response-types
- id_token,token,"id_token token"
- --grant-types
- implicit
- --scope
- openid,profile,email
- --callbacks
- http://localhost:3000
- --post-logout-callbacks
- http://localhost:3000/post-logout-callback
networks:
- hydra-net
deploy:
restart_policy:
condition: none
depends_on:
- hydra
hydra:
image: oryd/hydra:v1.0.0-rc.12
environment:
URLS_SELF_ISSUER: http://localhost:4444
URLS_SELF_PUBLIC: http://localhost:4444
URLS_LOGIN: http://localhost:8080/auth/login
URLS_CONSENT: http://localhost:8080/auth/consent
URLS_LOGOUT: http://localhost:8080/auth/logout
WEBFINGER_OIDC_DISCOVERY_SUPPORTED_SCOPES: profile,email,phone
WEBFINGER_OIDC_DISCOVERY_SUPPORTED_CLAIMS: name,family_name,given_name,nickname,email,phone_number
DSN: memory
command: serve all --dangerous-force-http
networks:
- hydra-net
ports:
- "4444:4444"
- "4445:4445"
deploy:
restart_policy:
condition: on-failure
depends_on:
- werther
werther:
image: icoreru/werther:v1.0.0
environment:
WERTHER_IDENTP_HYDRA_URL: http://hydra:4445
WERTHER_LDAP_ENDPOINTS: ldap:389
WERTHER_LDAP_BINDDN: cn=admin,dc=example,dc=com
WERTHER_LDAP_BINDPW: password
WERTHER_LDAP_BASEDN: "dc=example,dc=com"
WERTHER_LDAP_ROLE_BASEDN: "ou=AppRoles,dc=example,dc=com"
networks:
- hydra-net
ports:
- "8080:8080"
deploy:
restart_policy:
condition: on-failure
depends_on:
- ldap
ldap:
image: pgarrett/ldap-alpine
volumes:
- "./ldap.ldif:/ldif/ldap.ldif"
networks:
- hydra-net
ports:
- "389:389"
deploy:
restart_policy:
condition: on-failure
networks:
hydra-net:
```
3. Run the command:
```bash
docker stack deploy docker-compose.yml auth
```
4. Open the browser with http://localhost:4444/oauth2/auth?client_id=test-client&response_type=token&scope=openid%20profile%20email&state=12345678.
## Resources ## Resources
- [Introduction to ORY Hydra, OAuth 2.0, and OpenID Connect][hydra-doc]; - [Introduction to ORY Hydra, OAuth 2.0, and OpenID Connect][hydra-doc];
- [ORY Hydra: Integrating with (existing) User Management][hydra-login-consent]; - [ORY Hydra: Integrating with (existing) User Management][hydra-login-consent];
- [Official User Login & Consent Example](https://github.com/ory/hydra-login-consent-node); - [ORY Hydra: Configuration][hydra-doc-config];
- [ORY Hydra: Official User Login & Consent Example][hydra-login-consent-example];
- [OpenID Connect Core 1.0][oidc-spec-core]; - [OpenID Connect Core 1.0][oidc-spec-core];
- [OpenID Connect Session Management 1.0][oidc-spec-session]; - [OpenID Connect Session Management 1.0][oidc-spec-session];
- [OpenID Connect Front-Channel Logout 1.0][oidc-spec-front-channel-logout]; - [OpenID Connect Front-Channel Logout 1.0][oidc-spec-front-channel-logout];
@ -254,6 +328,9 @@ The code in this project is licensed under [MIT license][license].
[codecov-img]: https://codecov.io/gh/i-core/werther/branch/master/graph/badge.svg [codecov-img]: https://codecov.io/gh/i-core/werther/branch/master/graph/badge.svg
[codecov]: https://codecov.io/gh/i-core/werther [codecov]: https://codecov.io/gh/i-core/werther
[goreport-img]: https://goreportcard.com/badge/github.com/i-core/werther
[goreport]: https://goreportcard.com/report/github.com/i-core/werther
[contrib]: https://github.com/i-core/.github/blob/master/CONTRIBUTING.md [contrib]: https://github.com/i-core/.github/blob/master/CONTRIBUTING.md
[license]: LICENSE [license]: LICENSE
@ -263,6 +340,7 @@ The code in this project is licensed under [MIT license][license].
[hydra]: https://www.ory.sh/ [hydra]: https://www.ory.sh/
[hydra-doc]: https://www.ory.sh/docs/hydra/ [hydra-doc]: https://www.ory.sh/docs/hydra/
[hydra-login-consent]: https://www.ory.sh/docs/hydra/oauth2 [hydra-login-consent]: https://www.ory.sh/docs/hydra/oauth2
[hydra-login-consent-example]: https://github.com/ory/hydra-login-consent-node
[hydra-doc-config]: https://www.ory.sh/docs/hydra/configuration [hydra-doc-config]: https://www.ory.sh/docs/hydra/configuration
[oidc-spec-core]: https://openid.net/specs/openid-connect-core-1_0.html [oidc-spec-core]: https://openid.net/specs/openid-connect-core-1_0.html

View File

@ -30,8 +30,8 @@ var (
"b": "valB", "b": "valB",
"c": "valC", "c": "valC",
"roles": []map[string]interface{}{ "roles": []map[string]interface{}{
{"dn": "CN=role1,OU=app1,OU=test,DC=local", "test-roles-attr": "r1"}, {"dn": "cn=role1,ou=app1,ou=test,dc=local", "test-roles-attr": "r1"},
{"dn": "CN=role2,OU=app1,OU=test,DC=local", "test-roles-attr": "r2"}, {"dn": "cn=role2,ou=app1,ou=test,dc=local", "test-roles-attr": "r2"},
}, },
}, },
{ {
@ -41,10 +41,10 @@ var (
"b": "valB", "b": "valB",
"c": "valC", "c": "valC",
"roles": []map[string]interface{}{ "roles": []map[string]interface{}{
{"dn": "CN=role1,OU=app1,OU=test,DC=local", "test-roles-attr": "r1"}, {"dn": "cn=role1,ou=app1,ou=test,dc=local", "test-roles-attr": "r1"},
{"dn": "CN=role2,OU=app1,OU=test,DC=local", "test-roles-attr": "r2"}, {"dn": "cn=role2,ou=app1,ou=test,dc=local", "test-roles-attr": "r2"},
{"dn": "CN=role3,OU=app2,OU=test,DC=local", "test-roles-attr": "r3"}, {"dn": "cn=role3,ou=app2,ou=test,dc=local", "test-roles-attr": "r3"},
{"dn": "CN=role4,OU=app2,OU=test,DC=local", "test-roles-attr": "r4"}, {"dn": "cn=role4,ou=app2,ou=test,dc=local", "test-roles-attr": "r4"},
}, },
}, },
{ {
@ -54,7 +54,7 @@ var (
"b": "valB", "b": "valB",
"c": "valC", "c": "valC",
"roles": []map[string]interface{}{ "roles": []map[string]interface{}{
{"dn": "CN=role1,OU=app1,OU=test,DC=local", "test-roles-attr": "r1"}, {"dn": "cn=role1,ou=app1,ou=test,dc=local", "test-roles-attr": "r1"},
{"test-roles-attr": "r2"}, {"test-roles-attr": "r2"},
}, },
}, },
@ -65,8 +65,8 @@ var (
"b": "valB", "b": "valB",
"c": "valC", "c": "valC",
"roles": []map[string]interface{}{ "roles": []map[string]interface{}{
{"dn": "CN=role1,OU=app1,OU=test,DC=local", "test-roles-attr": "r1"}, {"dn": "cn=role1,ou=app1,ou=test,dc=local", "test-roles-attr": "r1"},
{"dn": "CN=role2,OU=app1,OU=test,DC=local"}, {"dn": "cn=role2,ou=app1,ou=test,dc=local"},
}, },
}, },
{ {
@ -76,7 +76,7 @@ var (
"b": "valB", "b": "valB",
"c": "valC", "c": "valC",
"roles": []map[string]interface{}{ "roles": []map[string]interface{}{
{"dn": "CN=role1,OU=test,DC=local", "test-roles-attr": "r1"}, {"dn": "cn=role1,ou=test,dc=local", "test-roles-attr": "r1"},
}, },
}, },
{ {
@ -375,7 +375,7 @@ func TestFindOIDCClaims(t *testing.T) {
BindDN: tc.bindDN, BindDN: tc.bindDN,
BindPass: tc.bindPass, BindPass: tc.bindPass,
AttrClaims: tc.attrClaims, AttrClaims: tc.attrClaims,
RoleBaseDN: "OU=test,DC=local", RoleBaseDN: "ou=test,dc=local",
RoleClaim: "test-roles-claim", RoleClaim: "test-roles-claim",
RoleAttr: "test-roles-attr", RoleAttr: "test-roles-attr",
}) })
@ -409,7 +409,7 @@ func TestClaimsCache(t *testing.T) {
client := New(Config{ client := New(Config{
Endpoints: connector.Endpoints(), Endpoints: connector.Endpoints(),
AttrClaims: map[string]string{"dn": "name", "a": "claimA", "d": "claimD"}, AttrClaims: map[string]string{"dn": "name", "a": "claimA", "d": "claimD"},
RoleBaseDN: "OU=test,DC=local", RoleBaseDN: "ou=test,dc=local",
RoleClaim: "test-roles-claim", RoleClaim: "test-roles-claim",
RoleAttr: "test-roles-attr", RoleAttr: "test-roles-attr",
}) })