move to GitHub.

This commit is contained in:
Nikolay Stupak
2019-05-24 16:13:15 +03:00
parent d761ad579a
commit 3bbac7bb74
28 changed files with 1840 additions and 336 deletions

332
README.md
View File

@ -1,21 +1,61 @@
[![GoDoc](https://godoc.das.i-core.ru/gopkg.i-core.ru/werther?status.svg)](https://godoc.das.i-core.ru/gopkg.i-core.ru/werther)
# Werther <sup>[1](#myfootnote1)</sup>
# Werther
[![GoDoc][doc-img]][doc] [![Build Status][build-img]][build] [![codecov][codecov-img]][codecov]
Werther is an identity provider for ORY Hydra that is an OAuth2 provider.
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.
**Important!**
**The current version is compatible with ORY Hydra v1.0.0-rc.12 or higher.**
![screenshot](.github/media/screenshot.gif)
## Build
**Features**
- Support [Active Directory][ad];
- Mapping LDAP attributes to OpenID Connect claims;
- Mapping LDAP groups to user roles;
- OAuth 2.0 scopes;
- Caching users roles;
- UI customization.
**Limitations**
- Werther grants all requested permissions to a client without displaying the consent page;
- Werther confirms a logout request without displaying the logout confirmation page.
**Requirements**
ORY Hydra v1.0.0-rc.12 or higher.
**Table of Contents**
<!-- To generate the table use the command "npx doctoc --maxlevel 2 README.md" -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
- [Installing](#installing)
- [Usage](#usage)
- [Configuration](#configuration)
- [User roles](#user-roles)
- [UI customization](#ui-customization)
- [Resources](#resources)
- [Footnotes](#footnotes)
- [Contributing](#contributing)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Installing
### From Docker
```bash
docker pull icoreru/werter
```
### From sources
```bash
go install ./...
```
## Development
Assume that your IP is set as $MY_HOST. The instruction will use 4444 TCP port for OAuth2 Provider Hydra,
3000 TCP port for Login Provider Werther, and 8080 TCP port for a callback. Tokens will be expired in ten minutes.
## Usage
1. Create a network:
```
@ -27,120 +67,206 @@ Assume that your IP is set as $MY_HOST. The instruction will use 4444 TCP port f
docker run --network hydra-net -d --restart always --name hydra \
-p 4444:4444 \
-p 4445:4445 \
-e OAUTH2_EXPOSE_INTERNAL_ERRORS=true \
-e LOG_LEVEL=debug \
-e TTL_ACCESS_TOKEN=10m \
-e TTL_ID_TOKEN=10m \
-e SERVE_PUBLIC_CORS_ENABLED=true \
-e SERVE_PUBLIC_CORS_ALLOWED_ORIGINS=http://$MY_HOST:8080 \
-e SERVE_PUBLIC_CORS_ALLOW_CREDENTIALS=true \
-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 URLS_SELF_ISSUER=http://localhost:4444 \
-e URLS_SELF_PUBLIC=http://localhost:4444 \
-e URLS_LOGIN=http://$MY_HOST:3000/auth/login \
-e URLS_CONSENT=http://$MY_HOST:3000/auth/consent \
-e URLS_LOGOUT=http://$MY_HOST:3000/auth/logout \
-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 --dangerous-force-http
oryd/hydra:v1.0.0-rc.12 serve all
```
You can learn additional properties with help command:
```
docker run -it --rm oryd/hydra:v1.0.0-rc.12 serve --help
```
Look for details in [ORY Hydra Configuration][hydra-doc-config] and [ORY Hydra Documentation][hydra-doc].
3. Register a client:
3. Run Werther:
```
docker run -it --rm --network hydra-net \
-e HYDRA_ADMIN_URL=http://hydra:4445 \
oryd/hydra:$HYDRA_VERSION 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://$MY_HOST:8080 \
--post-logout-callbacks http://$MY_HOST:8080/post-logout-callback
```
4. Run Werther:
```
docker run --network hydra-net -d --restart always --name werther -p 3000:8080 \
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.icore.local:389,icdc1.icore.local:389 \
-e WERTHER_LDAP_BINDDN=<BINDDN> \
-e WERTHER_LDAP_BINDPW=<BINDDN_PASSWORD> \
-e WERTHER_LDAP_BASEDN="DC=icore,DC=local" \
-e WERTHER_LDAP_ROLE_BASEDN="OU=AppRoles,OU=Domain Groups,DC=icore,DC=local" \
hub.das.i-core.ru/p/base-werther
-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
```
For all options see option help:
```
docker run -it --rm hub.das.i-core.ru/p/base-werther -help
```
## Configuration
5. Start an authentication process in a browser to get an access token:
```
open http://$MY_HOST:4444/oauth2/auth?client_id=test-client&response_type=token&scope=openid%20profile%20email&state=12345678
```
6. Start an authentication process in a browser to get an access token and id token:
```
open http://$MY_HOST:4444/oauth2/auth?client_id=test-client&response_type=id_token%20token&scope=openid%20profile%20email&state=12345678&nonce=87654321
```
The application is configured via environment variables.
Names of the environment variables starts with prefix `WERTHER_`.
See a list of the environment variables using the command:
7. Get user info:
```
http get "http://$MY_HOST:4444/userinfo" "Authorization: Bearer <ACCESS_TOKEN>"
```
```
werther -h
```
For example, you can get the next output:
```
HTTP/1.1 200 OK
Content-Length: 218
Content-Type: application/json
Date: Tue, 31 Jul 2018 17:17:51 GMT
Vary: Origin
## User roles
In LDAP user's roles are groups in which a user is a member.
The environment variable `WERTHER_LDAP_ROLE_DN` is a DN for searching roles.
For example, create an OU that repserents an application, and then in the created OU
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")
```
Run Werther with the environment variable `WERTHER_LDAP_ROLE_DN`
that equals to `OU=AppRoles,OU=Domain Groups,DC=local`.
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`.
```json
{
"https://github.com/i-core/werther/claims/roles": {
"App1": ["role1", "role2"],
}
}
```
To customize the roles claim's name you should set a value of the environment variable `WERTHER_LDAP_ROLE_CLAIM`.
For more details about claims naming see [OpenID Connect Core 1.0][oidc-spec-additional-claims].
**NB** There are cases when we need to create several roles with the same name in LDAP.
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")
```
Active Directory requires unique CNs in a domain. But in Active Directory
creating groups with the same CN in different OUs is difficult.
Because of it, Werther uses a LDAP attribute as a role's name instead of CN.
A name of a LDAP attribute is specified using the environment variable `WERTHER_LDAP_ROLE_ATTR`,
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`:
```json
{
"email": "klepa@i-core.ru",
"family_name": "Lepa",
"given_name": "Konstantin",
"http://i-core.ru/claims/roles": {
"HeraldTest1": [
"user"
]
},
"name": "Konstantin Lepa",
"sub": "CN=Konstantin Lepa,OU=Domain Users,DC=icore,DC=local"
"https://github.com/i-core/werther/claims/roles": {
"App1": ["role1", "role2"],
"App2": ["role1", "role2"]
}
}
```
* when the environment variable `WERTHER_LDAP_ROLE_DN` equals to `OU=Dev,OU=AppRoles,OU=Domain Groups,DC=local`:
```json
{
"https://github.com/i-core/werther/claims/roles": {
"App1": ["role1", "role3"],
"App2": ["role1", "role4"]
}
}
```
Look for details in [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter).
## UI customization
8. Re-get a token by httpie:
```
http --session u1 -F -v get \
"http://$MY_HOST:4444/oauth2/auth?client_id=test-client&response_type=token&scope=openid%20profile&state=12345678&prompt=none" \
"Cookie:<COOKIES_FROM_WERTHER_DOMAIN>"
```
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`:
9. Delete a user's session from a browser:
```
open "http://$MY_HOST:4444/oauth2/auth/sessions/login/revoke"
```
```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
```
10. Log a user out from a browser:
```
open http://$MY_HOST:4444/oauth2/sessions/logout?id_token_hint=<id_token>&post_logout_redirect_uri=http://$MY_HOST:8080/post-logout-callback&state=87654321
```
After a successful logout, a user will be redirected to the page "http://$MY_HOST:8080/post-logout-callback?state=87654321".
### Custom login page
A login page's template should contains blocks `title`, `style`, `script`, `content`.
Each block has access to data that is an object with the next properties:
- `CSRFToken` (string) - a CSRF token;
- `Challenge` (string) - a login challenge ID;
- `LoginURL` (string) - an endpoint that finishes the login process;
- `IsInvalidCredentials` (bool) - specifies that a user types an invalid username or password;
- `IsInternalError` (bool) specifies that an internal server error happens when finishing the login process.
11. (Optional) Sniff TCP packets between Hydra and Werther
```
docker run -it --rm --net=container:hydra nicolaka/netshoot tcpdump -i eth0 -A -nn port 4444
```
When a login page's template contains static resources (like styles, scripts, and images)
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).
## 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);
- [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];
- [OpenID Connect Back-Channel Logout 1.0][oidc-spec-back-channel-logout].
## Footnotes
1. <a name="myfootnote1"></a> Werther is named after robot Werther from [Guest from the Future](https://en.wikipedia.org/wiki/Guest_from_the_Future).
## Contributing
Thanks for your interest in contributing to this project.
Get started with our [Contributing Guide][contrib].
## License
The code in this project is licensed under [MIT license][license].
[doc-img]: https://godoc.org/github.com/i-core/werther?status.svg
[doc]: https://godoc.org/github.com/i-core/werther
[build-img]: https://travis-ci.com/i-core/werther.svg?branch=master
[build]: https://travis-ci.com/i-core/werther
[codecov-img]: https://codecov.io/gh/i-core/werther/branch/master/graph/badge.svg
[codecov]: https://codecov.io/gh/i-core/werther
[contrib]: https://github.com/i-core/.github/blob/master/CONTRIBUTING.md
[license]: LICENSE
[ldap]: https://ldap.com/
[ad]: https://docs.microsoft.com/ru-ru/windows/desktop/AD/active-directory-domain-services
[hydra]: https://www.ory.sh/
[hydra-doc]: https://www.ory.sh/docs/hydra/
[hydra-login-consent]: https://www.ory.sh/docs/hydra/oauth2
[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-additional-claims]: https://openid.net/specs/openid-connect-core-1_0.html#AdditionalClaims
[oidc-spec-session]: https://openid.net/specs/openid-connect-session-1_0.html
[oidc-spec-front-channel-logout]: https://openid.net/specs/openid-connect-frontchannel-1_0.html
[oidc-spec-back-channel-logout]: https://openid.net/specs/openid-connect-backchannel-1_0.html