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>
[![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].
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)
- [Usage](#usage)
- [Configuration](#configuration)
- [User roles](#user-roles)
- [UI customization](#ui-customization)
- [Example](#example)
- [Resources](#resources)
- [Footnotes](#footnotes)
- [Contributing](#contributing)
@ -46,7 +46,7 @@ ORY Hydra v1.0.0-rc.12 or higher.
### From Docker
```bash
docker pull icoreru/werter
docker pull icoreru/werther
```
### From sources
@ -55,44 +55,6 @@ docker pull icoreru/werter
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
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:
```
DC=local
|-- OU=Domain Groups
|-- OU=AppRoles
|-- OU=App1
|-- CN=app1_role1 (objectClass="group", description="role1")
|-- CN=app1_role2 (objectClass="group", description="role2")
dc=com
|-- dc=example
|-- ou=AppRoles
|-- ou=App1
|-- cn=app1_role1 (objectClass="group", description="role1")
|-- cn=app1_role2 (objectClass="group", description="role2")
```
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
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.
```
DC=local
|-- OU=Domain Groups
|-- OU=AppRoles
|-- OU=Test
|-- OU=App1
|-- CN=test_app1_role1 (objectClass="group", description="role1")
|-- CN=test_app1_role2 (objectClass="group", description="role2")
|-- OU=App2
|-- CN=test_app2_role1 (objectClass="group",description-"role1")
|-- CN=test_app2_role2 (objectClass="group",description-"role2")
|-- OU=Dev
|-- OU=App1
|-- CN=dev_app1_role1 (objectClass="group", description="role1")
|-- CN=dev_app1_role3 (objectClass="group", description="role3")
|-- OU=App2
|-- CN=dev_app2_role1 (objectClass="group",description-"role1")
|-- CN=dev_app2_role4 (objectClass="group",description-"role4")
dc=com
|-- dc=example
|-- ou=AppRoles
|-- ou=Test
|-- ou=App1
|-- cn=test_app1_role1 (objectClass="group", description="role1")
|-- cn=test_app1_role2 (objectClass="group", description="role2")
|-- ou=App2
|-- cn=test_app2_role1 (objectClass="group",description-"role1")
|-- cn=test_app2_role2 (objectClass="group",description-"role2")
|-- ou=Dev
|-- ou=App1
|-- cn=dev_app1_role1 (objectClass="group", description="role1")
|-- cn=dev_app1_role3 (objectClass="group", description="role3")
|-- ou=App2
|-- cn=dev_app2_role1 (objectClass="group",description-"role1")
|-- cn=dev_app2_role4 (objectClass="group",description-"role4")
```
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`.
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
{
"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
{
"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.
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`:
```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
```
After that you should set the directory path to the environment variable `WERTHER_WEB_DIR`.
### 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).
## 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
- [Introduction to ORY Hydra, OAuth 2.0, and OpenID Connect][hydra-doc];
- [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 Session Management 1.0][oidc-spec-session];
- [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]: 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
[license]: LICENSE
@ -263,6 +340,7 @@ The code in this project is licensed under [MIT license][license].
[hydra]: https://www.ory.sh/
[hydra-doc]: https://www.ory.sh/docs/hydra/
[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
[oidc-spec-core]: https://openid.net/specs/openid-connect-core-1_0.html

View File

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