forked from mystiq/dex
Merge branch 'master' into conformance_tests_improvements
This commit is contained in:
commit
f98332595e
241 changed files with 46456 additions and 10473 deletions
2
.github/workflows/.editorconfig
vendored
Normal file
2
.github/workflows/.editorconfig
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
[*.yml]
|
||||
indent_size = 2
|
79
.github/workflows/ci.yml
vendored
Normal file
79
.github/workflows/ci.yml
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-16.04
|
||||
env:
|
||||
GOFLAGS: -mod=readonly
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
ports:
|
||||
- 5432
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
etcd:
|
||||
image: gcr.io/etcd-development/etcd:v3.2.9
|
||||
ports:
|
||||
- 2379
|
||||
env:
|
||||
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
|
||||
ETCD_ADVERTISE_CLIENT_URLS: http://0.0.0.0:2379
|
||||
options: --health-cmd "ETCDCTL_API=3 etcdctl --endpoints http://localhost:2379 endpoint health" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
keystone:
|
||||
image: openio/openstack-keystone:pike
|
||||
ports:
|
||||
- 5000
|
||||
- 35357
|
||||
options: --health-cmd "curl --fail http://localhost:5000/v3" --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: Setup MySQL database
|
||||
run: mysql -u root -proot -e 'CREATE DATABASE dex;'
|
||||
|
||||
- name: Run tests
|
||||
run: make testall
|
||||
env:
|
||||
DEX_MYSQL_DATABASE: dex
|
||||
DEX_MYSQL_USER: root
|
||||
DEX_MYSQL_PASSWORD: root
|
||||
DEX_MYSQL_HOST: 127.0.0.1
|
||||
DEX_MYSQL_PORT: 3306
|
||||
DEX_POSTGRES_DATABASE: postgres
|
||||
DEX_POSTGRES_USER: postgres
|
||||
DEX_POSTGRES_PASSWORD: postgres
|
||||
DEX_POSTGRES_HOST: localhost
|
||||
DEX_POSTGRES_PORT: ${{ job.services.postgres.ports[5432] }}
|
||||
DEX_ETCD_ENDPOINTS: http://localhost:${{ job.services.etcd.ports[2379] }}
|
||||
DEX_LDAP_TESTS: 1
|
||||
DEX_KEYSTONE_URL: http://localhost:${{ job.services.keystone.ports[5000] }}
|
||||
DEX_KEYSTONE_ADMIN_URL: http://localhost:${{ job.services.keystone.ports[35357] }}
|
||||
DEX_KEYSTONE_ADMIN_USER: demo
|
||||
DEX_KEYSTONE_ADMIN_PASS: DEMO_PASS
|
||||
|
||||
- name: Run Kubernetes tests
|
||||
run: ./scripts/test-k8s.sh
|
||||
|
||||
- name: Run linter
|
||||
run: make lint
|
||||
|
||||
# Ensure proto generation doesn't depend on external packages.
|
||||
- name: Verify proto
|
||||
run: make verify-proto
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,3 +1,4 @@
|
|||
bin
|
||||
dist
|
||||
_output
|
||||
.idea
|
||||
|
|
35
.golangci.yml
Normal file
35
.golangci.yml
Normal file
|
@ -0,0 +1,35 @@
|
|||
run:
|
||||
skip-dirs:
|
||||
- vendor
|
||||
|
||||
linters-settings:
|
||||
golint:
|
||||
min-confidence: 0.1
|
||||
goimports:
|
||||
local-prefixes: github.com/dexidp/dex
|
||||
|
||||
linters:
|
||||
enable-all: true
|
||||
disable:
|
||||
- funlen
|
||||
- maligned
|
||||
- wsl
|
||||
|
||||
# TODO: fix me
|
||||
- unparam
|
||||
- golint
|
||||
- goconst
|
||||
- staticcheck
|
||||
- nakedret
|
||||
- errcheck
|
||||
- gosec
|
||||
- gochecknoinits
|
||||
- gochecknoglobals
|
||||
- prealloc
|
||||
- scopelint
|
||||
- lll
|
||||
- dupl
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gocognit
|
||||
- godox
|
|
@ -6,7 +6,7 @@ dist: xenial
|
|||
|
||||
matrix:
|
||||
include:
|
||||
- go: '1.12.x'
|
||||
- go: '1.13.x'
|
||||
|
||||
env:
|
||||
global:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
FROM golang:1.12.9-alpine
|
||||
FROM golang:1.13-alpine
|
||||
|
||||
RUN apk add --no-cache --update alpine-sdk
|
||||
|
||||
COPY . /go/src/github.com/dexidp/dex
|
||||
RUN cd /go/src/github.com/dexidp/dex && make release-binary
|
||||
|
||||
FROM alpine:3.9
|
||||
FROM alpine:3.10
|
||||
# Dex connectors, such as GitHub and Google logins require root certificates.
|
||||
# Proper installations should manage those certificates, but it's a bad user
|
||||
# experience when this doesn't work out of the box.
|
||||
|
|
60
Documentation/connectors/google.md
Normal file
60
Documentation/connectors/google.md
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Authentication through Google
|
||||
|
||||
## Overview
|
||||
|
||||
Dex is able to use Google's OpenID Connect provider as an authentication source.
|
||||
|
||||
The connector uses the same authentication flow as the OpenID Connect provider but adds Google specific features such as Hosted domain support and reading groups using a service account.
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
connectors:
|
||||
- type: google
|
||||
id: google
|
||||
name: Google
|
||||
config:
|
||||
|
||||
# Connector config values starting with a "$" will read from the environment.
|
||||
clientID: $GOOGLE_CLIENT_ID
|
||||
clientSecret: $GOOGLE_CLIENT_SECRET
|
||||
|
||||
# Dex's issuer URL + "/callback"
|
||||
redirectURI: http://127.0.0.1:5556/callback
|
||||
|
||||
# Google supports whitelisting allowed domains when using G Suite
|
||||
# (Google Apps). The following field can be set to a list of domains
|
||||
# that can log in:
|
||||
#
|
||||
# hostedDomains:
|
||||
# - example.com
|
||||
|
||||
# The Google connector supports whitelisting allowed groups when using G Suite
|
||||
# (Google Apps). The following field can be set to a list of groups
|
||||
# that can log in:
|
||||
#
|
||||
# groups:
|
||||
# - admins@example.com
|
||||
|
||||
# Google does not support the OpenID Connect groups claim and only supports
|
||||
# fetching a user's group membership with a service account.
|
||||
# This service account requires an authentication JSON file and the email
|
||||
# of a G Suite admin to impersonate:
|
||||
#
|
||||
#serviceAccountFilePath: googleAuth.json
|
||||
#adminEmail: super-user@example.com
|
||||
```
|
||||
|
||||
## Fetching groups from Google
|
||||
To allow Dex to fetch group information from Google, you will need to configure a service account for Dex to use.
|
||||
This account needs Domain-Wide Delegation and permission to access the `https://www.googleapis.com/auth/admin.directory.group.readonly` API scope.
|
||||
|
||||
To get group fetching set up:
|
||||
|
||||
1. Follow the [instructions](https://developers.google.com/admin-sdk/directory/v1/guides/delegation) to set up a service account with Domain-Wide Delegation
|
||||
- During service account creation, a JSON key file will be created that contains authentication information for the service account. This needs storing in a location accessible by Dex and you will set the `serviceAccountFilePath` to point at it.
|
||||
- When delegating the API scopes to the service account, delegate the `https://www.googleapis.com/auth/admin.directory.group.readonly` scope and only this scope. If you delegate more scopes to the service account, it will not be able to access the API.
|
||||
2. Enable the [Admin SDK](https://console.developers.google.com/apis/library/admin.googleapis.com/)
|
||||
3. Add the `serviceAccountFilePath` and `adminEmail` configuration options to your Dex config.
|
||||
- `serviceAccountFilePath` should point to the location of the service account JSON key file
|
||||
- `adminEmail` should be the email of a G Suite super user. The service account you created earlier will impersonate this user when making calls to the admin API. A valid user should be able to retrieve a list of groups when [testing the API](https://developers.google.com/admin-sdk/directory/v1/reference/groups/list#try-it).
|
|
@ -13,7 +13,7 @@ The connector executes two primary queries:
|
|||
|
||||
The dex repo contains a basic LDAP setup using [OpenLDAP][openldap].
|
||||
|
||||
First start the LDAP server using the example script. This will run the OpenLDAP daemon and seed it with a initial set of users.
|
||||
First start the LDAP server using the example script. This will run the OpenLDAP daemon and seed it with an initial set of users.
|
||||
|
||||
```
|
||||
./scripts/slapd.sh
|
||||
|
@ -89,7 +89,7 @@ connectors:
|
|||
# server provides access for anonymous auth.
|
||||
# Please note that if the bind password contains a `$`, it has to be saved in an
|
||||
# environment variable which should be given as the value to `bindPW`.
|
||||
bindDN: uid=seviceaccount,cn=users,dc=example,dc=com
|
||||
bindDN: uid=serviceaccount,cn=users,dc=example,dc=com
|
||||
bindPW: password
|
||||
|
||||
# The attribute to display in the provided password prompt. If unset, will
|
||||
|
|
|
@ -10,7 +10,7 @@ Prominent examples of OpenID Connect providers include Google Accounts, Salesfor
|
|||
|
||||
This connector does not support the "groups" claim. Progress for this is tracked in [issue #1065][issue-1065].
|
||||
|
||||
When using refresh tokens, changes to the upstream claims aren't propegated to the id_token returned by dex. If a user's email changes, the "email" claim returned by dex won't change unless the user logs in again. Progress for this is tracked in [issue #863][issue-863].
|
||||
When using refresh tokens, changes to the upstream claims aren't propagated to the id_token returned by dex. If a user's email changes, the "email" claim returned by dex won't change unless the user logs in again. Progress for this is tracked in [issue #863][issue-863].
|
||||
|
||||
## Configuration
|
||||
|
||||
|
@ -36,7 +36,7 @@ connectors:
|
|||
|
||||
# Some providers require passing client_secret via POST parameters instead
|
||||
# of basic auth, despite the OAuth2 RFC discouraging it. Many of these
|
||||
# cases are caught internally, but some may need to uncommented the
|
||||
# cases are caught internally, but some may need to uncomment the
|
||||
# following field.
|
||||
#
|
||||
# basicAuthUnsupported: true
|
||||
|
@ -56,11 +56,18 @@ connectors:
|
|||
# - email
|
||||
# - groups
|
||||
|
||||
# Some providers return claims without "email_verified", when they had no usage of emails verification in enrollement process
|
||||
# Some providers return claims without "email_verified", when they had no usage of emails verification in enrollment process
|
||||
# or if they are acting as a proxy for another IDP etc AWS Cognito with an upstream SAML IDP
|
||||
# This can be overridden with the below option
|
||||
# insecureSkipEmailVerified: true
|
||||
|
||||
# Groups claims (like the rest of oidc claims through dex) only refresh when the id token is refreshed
|
||||
# meaning the regular refresh flow doesn't update the groups claim. As such by default the oidc connector
|
||||
# doesn't allow groups claims. If you are okay with having potentially stale group claims you can use
|
||||
# this option to enable groups claims through the oidc connector on a per-connector basis.
|
||||
# This can be overridden with the below option
|
||||
# insecureEnableGroups: true
|
||||
|
||||
# When enabled, the OpenID Connector will query the UserInfo endpoint for additional claims. UserInfo claims
|
||||
# take priority over claims returned by the IDToken. This option should be used when the IDToken doesn't contain
|
||||
# all the claims requested.
|
||||
|
|
|
@ -14,6 +14,10 @@ __The connector doesn't support refresh tokens__ since the SAML 2.0 protocol doe
|
|||
|
||||
The connector doesn't support signed AuthnRequests or encrypted attributes.
|
||||
|
||||
## Group Filtering
|
||||
|
||||
The SAML Connector supports providing a whitelist of SAML Groups to filter access based on, and when the `groupsattr` is set with a scope including groups, Dex will check for membership based on configured groups in the `allowedGroups` config setting for the SAML connector.
|
||||
|
||||
## Configuration
|
||||
|
||||
```yaml
|
||||
|
@ -44,6 +48,10 @@ connectors:
|
|||
emailAttr: email
|
||||
groupsAttr: groups # optional
|
||||
|
||||
# List of groups to filter access based on membership
|
||||
# allowedGroups
|
||||
# - Admins
|
||||
|
||||
# CA's can also be provided inline as a base64'd blob.
|
||||
#
|
||||
# caData: ( RAW base64'd PEM encoded CA )
|
||||
|
|
|
@ -156,6 +156,9 @@ Once the example app is running, choose the GitHub option and grant access to de
|
|||
|
||||
The default redirect uri is http://127.0.0.1:5555/callback and can be changed with the `--redirect-uri` flag and should correspond with your configmap.
|
||||
|
||||
Please note the redirect uri is different from the one you filled when creating `GitHub OAuth2 client credentials`.
|
||||
When you login, GitHub first redirects to dex (https://dex.example.com:32000/callback), then dex redirects to the redirect uri of exampl-app.
|
||||
|
||||
The printed ID Token can then be used as a bearer token to authenticate against the API server.
|
||||
|
||||
```
|
||||
|
|
|
@ -92,7 +92,7 @@ OpenID Connect servers have a discovery mechanism for OAuth2 endpoints, scopes
|
|||
supported, and indications of various other OpenID Connect features.
|
||||
|
||||
```
|
||||
$ curl http://127.0.0.1:5556/.well-known/openid-configuration
|
||||
$ curl http://127.0.0.1:5556/dex/.well-known/openid-configuration
|
||||
{
|
||||
"issuer": "http://127.0.0.1:5556",
|
||||
"authorization_endpoint": "http://127.0.0.1:5556/auth",
|
||||
|
@ -123,7 +123,7 @@ https://tools.ietf.org/html/rfc7517) Set of public keys that will look
|
|||
something like this:
|
||||
|
||||
```
|
||||
$ curl http://127.0.0.1:5556/keys
|
||||
$ curl http://127.0.0.1:5556/dex/keys
|
||||
{
|
||||
"keys": [
|
||||
{
|
||||
|
|
|
@ -11,7 +11,7 @@ in with GitHub.
|
|||
|
||||
## The problem
|
||||
|
||||
When dex is federaing to an upstream identity provider (IDP), we want to ensure
|
||||
When dex is federating to an upstream identity provider (IDP), we want to ensure
|
||||
claims being passed onto clients remain fresh. This includes data such as Google
|
||||
accounts display names, LDAP group membership, account deactivations. Changes to
|
||||
these on an upstream IDP should always be reflected in the claims dex passes to
|
||||
|
|
|
@ -14,11 +14,13 @@ Steps:
|
|||
```yaml
|
||||
frontend:
|
||||
dir: /path/to/custom/web
|
||||
issuer: my-dex
|
||||
extra:
|
||||
tos_footer_link: "https://example.com/terms"
|
||||
client_logo_url: "../theme/client-logo.png"
|
||||
foo: "bar"
|
||||
```
|
||||
5. Set the `frontend.dir` value to your own `web` directory.
|
||||
6. Write the issuer in the `issuer` directory in order to modify the Dex title and the `Log in to <<dex>>` tag.
|
||||
|
||||
To test your templates simply run Dex with a valid configuration and go through a login flow.
|
|
@ -1,4 +1,5 @@
|
|||
Rithu John <rithujohn191@gmail.com> (@rithujohn191)
|
||||
Stephan Renatus <srenatus@chef.io> (@srenatus)
|
||||
Joel Speed <Joel.speed@hotmail.co.uk> (@JoelSpeed)
|
||||
Nandor Kracser <bonifaido@gmail.com> (@bonifaido)
|
||||
Nandor Kracser <nandor@banzaicloud.com> (@bonifaido)
|
||||
Mark Sagi-Kazar <mark@banzaicloud.com> (@sagikazarmark)
|
||||
|
|
28
Makefile
28
Makefile
|
@ -2,7 +2,6 @@ PROJ=dex
|
|||
ORG_PATH=github.com/dexidp
|
||||
REPO_PATH=$(ORG_PATH)/$(PROJ)
|
||||
export PATH := $(PWD)/bin:$(PATH)
|
||||
THIS_DIRECTORY:=$(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
|
||||
VERSION ?= $(shell ./scripts/git-version)
|
||||
|
||||
|
@ -18,6 +17,9 @@ export GOBIN=$(PWD)/bin
|
|||
|
||||
LD_FLAGS="-w -X $(REPO_PATH)/version.Version=$(VERSION)"
|
||||
|
||||
# Dependency versions
|
||||
GOLANGCI_VERSION = 1.21.0
|
||||
|
||||
build: bin/dex bin/example-app bin/grpc-client
|
||||
|
||||
bin/dex:
|
||||
|
@ -45,14 +47,19 @@ test:
|
|||
testrace:
|
||||
@go test -v --race ./...
|
||||
|
||||
vet:
|
||||
@go vet ./...
|
||||
bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
|
||||
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
|
||||
bin/golangci-lint-${GOLANGCI_VERSION}:
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | BINARY=golangci-lint bash -s -- v${GOLANGCI_VERSION}
|
||||
@mv bin/golangci-lint $@
|
||||
|
||||
fmt:
|
||||
@./scripts/gofmt ./...
|
||||
.PHONY: lint
|
||||
lint: bin/golangci-lint ## Run linter
|
||||
bin/golangci-lint run
|
||||
|
||||
lint: bin/golint
|
||||
@./bin/golint -set_exit_status $(shell go list ./...)
|
||||
.PHONY: fix
|
||||
fix: bin/golangci-lint ## Fix lint violations
|
||||
bin/golangci-lint run --fix
|
||||
|
||||
.PHONY: docker-image
|
||||
docker-image:
|
||||
|
@ -73,14 +80,11 @@ bin/protoc: scripts/get-protoc
|
|||
bin/protoc-gen-go:
|
||||
@go install -v $(REPO_PATH)/vendor/github.com/golang/protobuf/protoc-gen-go
|
||||
|
||||
bin/golint:
|
||||
@go install -v $(THIS_DIRECTORY)/vendor/golang.org/x/lint/golint
|
||||
|
||||
clean:
|
||||
@rm -rf bin/
|
||||
|
||||
testall: testrace vet fmt lint
|
||||
testall: testrace
|
||||
|
||||
FORCE:
|
||||
|
||||
.PHONY: test testrace vet fmt lint testall
|
||||
.PHONY: test testrace testall
|
||||
|
|
22
README.md
22
README.md
|
@ -63,17 +63,17 @@ Depending on the connectors limitations in protocols can prevent dex from issuin
|
|||
|
||||
Dex implements the following connectors:
|
||||
|
||||
| Name | supports refresh tokens | supports groups claim | status | notes |
|
||||
| ---- | ----------------------- | --------------------- | ------ | ----- |
|
||||
| [LDAP](Documentation/connectors/ldap.md) | yes | yes | stable | |
|
||||
| [GitHub](Documentation/connectors/github.md) | yes | yes | stable | |
|
||||
| [SAML 2.0](Documentation/connectors/saml.md) | no | yes | stable |
|
||||
| [GitLab](Documentation/connectors/gitlab.md) | yes | yes | beta | |
|
||||
| [OpenID Connect](Documentation/connectors/oidc.md) | yes | no ([#1065][issue-1065]) | beta | Includes Google, Salesforce, Azure, etc. |
|
||||
| [LinkedIn](Documentation/connectors/linkedin.md) | yes | no | beta | |
|
||||
| [Microsoft](Documentation/connectors/microsoft.md) | yes | yes | beta | |
|
||||
| [AuthProxy](Documentation/connectors/authproxy.md) | no | no | alpha | Authentication proxies such as Apache2 mod_auth, etc. |
|
||||
| [Bitbucket Cloud](Documentation/connectors/bitbucketcloud.md) | yes | yes | alpha | |
|
||||
| Name | supports refresh tokens | supports groups claim | supports preferred_username claim | status | notes |
|
||||
| ---- | ----------------------- | --------------------- | --------------------------------- | ------ | ----- |
|
||||
| [LDAP](Documentation/connectors/ldap.md) | yes | yes | yes | stable | |
|
||||
| [GitHub](Documentation/connectors/github.md) | yes | yes | yes | stable | |
|
||||
| [SAML 2.0](Documentation/connectors/saml.md) | no | yes | no | stable |
|
||||
| [GitLab](Documentation/connectors/gitlab.md) | yes | yes | yes | beta | |
|
||||
| [OpenID Connect](Documentation/connectors/oidc.md) | yes | no ([#1065][issue-1065]) | no | beta | Includes Google, Salesforce, Azure, etc. |
|
||||
| [LinkedIn](Documentation/connectors/linkedin.md) | yes | no | no | beta | |
|
||||
| [Microsoft](Documentation/connectors/microsoft.md) | yes | yes | no | beta | |
|
||||
| [AuthProxy](Documentation/connectors/authproxy.md) | no | no | no | alpha | Authentication proxies such as Apache2 mod_auth, etc. |
|
||||
| [Bitbucket Cloud](Documentation/connectors/bitbucketcloud.md) | yes | yes | no | alpha | |
|
||||
|
||||
Stable, beta, and alpha are defined as:
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/dexidp/dex/server"
|
||||
"testing"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
|
@ -9,6 +8,7 @@ import (
|
|||
|
||||
"github.com/dexidp/dex/connector/mock"
|
||||
"github.com/dexidp/dex/connector/oidc"
|
||||
"github.com/dexidp/dex/server"
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/dexidp/dex/storage/sql"
|
||||
)
|
||||
|
@ -211,5 +211,4 @@ logger:
|
|||
if diff := pretty.Compare(c, want); diff != "" {
|
||||
t.Errorf("got!=want: %s", diff)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -182,7 +182,6 @@ func serve(cmd *cobra.Command, args []string) error {
|
|||
return fmt.Errorf("failed to initialize storage connectors: %v", err)
|
||||
}
|
||||
storageConnectors[i] = conn
|
||||
|
||||
}
|
||||
|
||||
if c.EnablePasswordDB {
|
||||
|
|
|
@ -143,7 +143,7 @@ func cmd() *cobra.Command {
|
|||
ctx := oidc.ClientContext(context.Background(), a.client)
|
||||
provider, err := oidc.NewProvider(ctx, issuerURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to query provider %q: %v", issuerURL, err)
|
||||
return fmt.Errorf("failed to query provider %q: %v", issuerURL, err)
|
||||
}
|
||||
|
||||
var s struct {
|
||||
|
@ -153,7 +153,7 @@ func cmd() *cobra.Command {
|
|||
ScopesSupported []string `json:"scopes_supported"`
|
||||
}
|
||||
if err := provider.Claims(&s); err != nil {
|
||||
return fmt.Errorf("Failed to parse provider scopes_supported: %v", err)
|
||||
return fmt.Errorf("failed to parse provider scopes_supported: %v", err)
|
||||
}
|
||||
|
||||
if len(s.ScopesSupported) == 0 {
|
||||
|
|
|
@ -41,7 +41,6 @@ type Config struct {
|
|||
|
||||
// Open returns a strategy for logging in through Bitbucket.
|
||||
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
|
||||
|
||||
b := bitbucketConnector{
|
||||
redirectURI: c.RedirectURI,
|
||||
teams: c.Teams,
|
||||
|
@ -373,7 +372,6 @@ type userTeamsResponse struct {
|
|||
}
|
||||
|
||||
func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client) ([]string, error) {
|
||||
|
||||
var teams []string
|
||||
apiURL := b.apiURL + "/teams?role=member"
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
)
|
||||
|
||||
func TestUserGroups(t *testing.T) {
|
||||
|
||||
teamsResponse := userTeamsResponse{
|
||||
pagedResponse: pagedResponse{
|
||||
Size: 3,
|
||||
|
@ -46,7 +45,6 @@ func TestUserGroups(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUserWithoutTeams(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/teams?role=member": userTeamsResponse{},
|
||||
})
|
||||
|
@ -61,7 +59,6 @@ func TestUserWithoutTeams(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/user": user{Username: "some-login"},
|
||||
"/user/emails": userEmailResponse{
|
||||
|
|
|
@ -23,10 +23,11 @@ type Scopes struct {
|
|||
|
||||
// Identity represents the ID Token claims supported by the server.
|
||||
type Identity struct {
|
||||
UserID string
|
||||
Username string
|
||||
Email string
|
||||
EmailVerified bool
|
||||
UserID string
|
||||
Username string
|
||||
PreferredUsername string
|
||||
Email string
|
||||
EmailVerified bool
|
||||
|
||||
Groups []string
|
||||
|
||||
|
|
|
@ -67,7 +67,6 @@ type Org struct {
|
|||
|
||||
// Open returns a strategy for logging in through GitHub.
|
||||
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
|
||||
|
||||
if c.Org != "" {
|
||||
// Return error if both 'org' and 'orgs' fields are used.
|
||||
if len(c.Orgs) > 0 {
|
||||
|
@ -107,7 +106,6 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
|
|||
if g.httpClient, err = newHTTPClient(g.rootCA); err != nil {
|
||||
return nil, fmt.Errorf("failed to create HTTP client: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
g.loadAllGroups = c.LoadAllGroups
|
||||
|
||||
|
@ -144,7 +142,7 @@ type githubConnector struct {
|
|||
hostName string
|
||||
// Used to support untrusted/self-signed CA certs.
|
||||
rootCA string
|
||||
// HTTP Client that trusts the custom delcared rootCA cert.
|
||||
// HTTP Client that trusts the custom declared rootCA cert.
|
||||
httpClient *http.Client
|
||||
// optional choice between 'name' (default) or 'slug'
|
||||
teamNameField string
|
||||
|
@ -206,7 +204,7 @@ func (e *oauth2Error) Error() string {
|
|||
return e.error + ": " + e.errorDescription
|
||||
}
|
||||
|
||||
// newHTTPClient returns a new HTTP client that trusts the custom delcared rootCA cert.
|
||||
// newHTTPClient returns a new HTTP client that trusts the custom declared rootCA cert.
|
||||
func newHTTPClient(rootCA string) (*http.Client, error) {
|
||||
tlsConfig := tls.Config{RootCAs: x509.NewCertPool()}
|
||||
rootCABytes, err := ioutil.ReadFile(rootCA)
|
||||
|
@ -266,10 +264,11 @@ func (c *githubConnector) HandleCallback(s connector.Scopes, r *http.Request) (i
|
|||
}
|
||||
|
||||
identity = connector.Identity{
|
||||
UserID: strconv.Itoa(user.ID),
|
||||
Username: username,
|
||||
Email: user.Email,
|
||||
EmailVerified: true,
|
||||
UserID: strconv.Itoa(user.ID),
|
||||
Username: username,
|
||||
PreferredUsername: user.Login,
|
||||
Email: user.Email,
|
||||
EmailVerified: true,
|
||||
}
|
||||
if c.useLoginAsID {
|
||||
identity.UserID = user.Login
|
||||
|
@ -317,6 +316,7 @@ func (c *githubConnector) Refresh(ctx context.Context, s connector.Scopes, ident
|
|||
username = user.Login
|
||||
}
|
||||
identity.Username = username
|
||||
identity.PreferredUsername = user.Login
|
||||
identity.Email = user.Email
|
||||
|
||||
// Only set identity.Groups if 'orgs', 'org', or 'groups' scope are specified.
|
||||
|
|
|
@ -126,7 +126,6 @@ func TestUserGroupsWithTeamNameAndSlugFieldConfig(t *testing.T) {
|
|||
|
||||
// tests that the users login is used as their username when they have no username set
|
||||
func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]testResponse{
|
||||
"/user": {data: user{Login: "some-login", ID: 12345678}},
|
||||
"/user/emails": {data: []userEmail{{
|
||||
|
@ -168,7 +167,6 @@ func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoginUsedAsIDWhenConfigured(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]testResponse{
|
||||
"/user": {data: user{Login: "some-login", ID: 12345678, Name: "Joe Bloggs"}},
|
||||
"/user/emails": {data: []userEmail{{
|
||||
|
|
|
@ -147,10 +147,11 @@ func (c *gitlabConnector) HandleCallback(s connector.Scopes, r *http.Request) (i
|
|||
username = user.Email
|
||||
}
|
||||
identity = connector.Identity{
|
||||
UserID: strconv.Itoa(user.ID),
|
||||
Username: username,
|
||||
Email: user.Email,
|
||||
EmailVerified: true,
|
||||
UserID: strconv.Itoa(user.ID),
|
||||
Username: username,
|
||||
PreferredUsername: user.Username,
|
||||
Email: user.Email,
|
||||
EmailVerified: true,
|
||||
}
|
||||
if c.useLoginAsID {
|
||||
identity.UserID = user.Username
|
||||
|
@ -197,6 +198,7 @@ func (c *gitlabConnector) Refresh(ctx context.Context, s connector.Scopes, ident
|
|||
username = user.Email
|
||||
}
|
||||
ident.Username = username
|
||||
ident.PreferredUsername = user.Username
|
||||
ident.Email = user.Email
|
||||
|
||||
if c.groupsRequired(s.Groups) {
|
||||
|
|
|
@ -65,7 +65,6 @@ func TestUserGroupsWithoutOrgs(t *testing.T) {
|
|||
|
||||
// tests that the email is used as their username when they have no username set
|
||||
func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678},
|
||||
"/oauth/token": map[string]interface{}{
|
||||
|
@ -102,7 +101,6 @@ func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoginUsedAsIDWhenConfigured(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs", Username: "joebloggs"},
|
||||
"/oauth/token": map[string]interface{}{
|
||||
|
@ -130,7 +128,6 @@ func TestLoginUsedAsIDWhenConfigured(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoginWithTeamWhitelisted(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs"},
|
||||
"/oauth/token": map[string]interface{}{
|
||||
|
@ -158,7 +155,6 @@ func TestLoginWithTeamWhitelisted(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestLoginWithTeamNonWhitelisted(t *testing.T) {
|
||||
|
||||
s := newTestServer(map[string]interface{}{
|
||||
"/api/v4/user": gitlabUser{Email: "some@email.com", ID: 12345678, Name: "Joe Bloggs", Username: "joebloggs"},
|
||||
"/oauth/token": map[string]interface{}{
|
||||
|
|
282
connector/google/google.go
Normal file
282
connector/google/google.go
Normal file
|
@ -0,0 +1,282 @@
|
|||
// Package google implements logging in through Google's OpenID Connect provider.
|
||||
package google
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"golang.org/x/oauth2"
|
||||
"golang.org/x/oauth2/google"
|
||||
admin "google.golang.org/api/admin/directory/v1"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
pkg_groups "github.com/dexidp/dex/pkg/groups"
|
||||
"github.com/dexidp/dex/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
issuerURL = "https://accounts.google.com"
|
||||
)
|
||||
|
||||
// Config holds configuration options for Google logins.
|
||||
type Config struct {
|
||||
ClientID string `json:"clientID"`
|
||||
ClientSecret string `json:"clientSecret"`
|
||||
RedirectURI string `json:"redirectURI"`
|
||||
|
||||
Scopes []string `json:"scopes"` // defaults to "profile" and "email"
|
||||
|
||||
// Optional list of whitelisted domains
|
||||
// If this field is nonempty, only users from a listed domain will be allowed to log in
|
||||
HostedDomains []string `json:"hostedDomains"`
|
||||
|
||||
// Optional list of whitelisted groups
|
||||
// If this field is nonempty, only users from a listed group will be allowed to log in
|
||||
Groups []string `json:"groups"`
|
||||
|
||||
// Optional path to service account json
|
||||
// If nonempty, and groups claim is made, will use authentication from file to
|
||||
// check groups with the admin directory api
|
||||
ServiceAccountFilePath string `json:"serviceAccountFilePath"`
|
||||
|
||||
// Required if ServiceAccountFilePath
|
||||
// The email of a GSuite super user which the service account will impersonate
|
||||
// when listing groups
|
||||
AdminEmail string
|
||||
}
|
||||
|
||||
// Open returns a connector which can be used to login users through Google.
|
||||
func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
provider, err := oidc.NewProvider(ctx, issuerURL)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, fmt.Errorf("failed to get provider: %v", err)
|
||||
}
|
||||
|
||||
scopes := []string{oidc.ScopeOpenID}
|
||||
if len(c.Scopes) > 0 {
|
||||
scopes = append(scopes, c.Scopes...)
|
||||
} else {
|
||||
scopes = append(scopes, "profile", "email")
|
||||
}
|
||||
|
||||
srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail)
|
||||
if err != nil {
|
||||
cancel()
|
||||
return nil, fmt.Errorf("could not create directory service: %v", err)
|
||||
}
|
||||
|
||||
clientID := c.ClientID
|
||||
return &googleConnector{
|
||||
redirectURI: c.RedirectURI,
|
||||
oauth2Config: &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: c.ClientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: scopes,
|
||||
RedirectURL: c.RedirectURI,
|
||||
},
|
||||
verifier: provider.Verifier(
|
||||
&oidc.Config{ClientID: clientID},
|
||||
),
|
||||
logger: logger,
|
||||
cancel: cancel,
|
||||
hostedDomains: c.HostedDomains,
|
||||
groups: c.Groups,
|
||||
serviceAccountFilePath: c.ServiceAccountFilePath,
|
||||
adminEmail: c.AdminEmail,
|
||||
adminSrv: srv,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
_ connector.CallbackConnector = (*googleConnector)(nil)
|
||||
_ connector.RefreshConnector = (*googleConnector)(nil)
|
||||
)
|
||||
|
||||
type googleConnector struct {
|
||||
redirectURI string
|
||||
oauth2Config *oauth2.Config
|
||||
verifier *oidc.IDTokenVerifier
|
||||
cancel context.CancelFunc
|
||||
logger log.Logger
|
||||
hostedDomains []string
|
||||
groups []string
|
||||
serviceAccountFilePath string
|
||||
adminEmail string
|
||||
adminSrv *admin.Service
|
||||
}
|
||||
|
||||
func (c *googleConnector) Close() error {
|
||||
c.cancel()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *googleConnector) LoginURL(s connector.Scopes, callbackURL, state string) (string, error) {
|
||||
if c.redirectURI != callbackURL {
|
||||
return "", fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI)
|
||||
}
|
||||
|
||||
var opts []oauth2.AuthCodeOption
|
||||
if len(c.hostedDomains) > 0 {
|
||||
preferredDomain := c.hostedDomains[0]
|
||||
if len(c.hostedDomains) > 1 {
|
||||
preferredDomain = "*"
|
||||
}
|
||||
opts = append(opts, oauth2.SetAuthURLParam("hd", preferredDomain))
|
||||
}
|
||||
|
||||
if s.OfflineAccess {
|
||||
opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent"))
|
||||
}
|
||||
return c.oauth2Config.AuthCodeURL(state, opts...), nil
|
||||
}
|
||||
|
||||
type oauth2Error struct {
|
||||
error string
|
||||
errorDescription string
|
||||
}
|
||||
|
||||
func (e *oauth2Error) Error() string {
|
||||
if e.errorDescription == "" {
|
||||
return e.error
|
||||
}
|
||||
return e.error + ": " + e.errorDescription
|
||||
}
|
||||
|
||||
func (c *googleConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
|
||||
q := r.URL.Query()
|
||||
if errType := q.Get("error"); errType != "" {
|
||||
return identity, &oauth2Error{errType, q.Get("error_description")}
|
||||
}
|
||||
token, err := c.oauth2Config.Exchange(r.Context(), q.Get("code"))
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("google: failed to get token: %v", err)
|
||||
}
|
||||
|
||||
return c.createIdentity(r.Context(), identity, s, token)
|
||||
}
|
||||
|
||||
func (c *googleConnector) Refresh(ctx context.Context, s connector.Scopes, identity connector.Identity) (connector.Identity, error) {
|
||||
t := &oauth2.Token{
|
||||
RefreshToken: string(identity.ConnectorData),
|
||||
Expiry: time.Now().Add(-time.Hour),
|
||||
}
|
||||
token, err := c.oauth2Config.TokenSource(ctx, t).Token()
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("google: failed to get token: %v", err)
|
||||
}
|
||||
|
||||
return c.createIdentity(ctx, identity, s, token)
|
||||
}
|
||||
|
||||
func (c *googleConnector) createIdentity(ctx context.Context, identity connector.Identity, s connector.Scopes, token *oauth2.Token) (connector.Identity, error) {
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return identity, errors.New("google: no id_token in token response")
|
||||
}
|
||||
idToken, err := c.verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("google: failed to verify ID Token: %v", err)
|
||||
}
|
||||
|
||||
var claims struct {
|
||||
Username string `json:"name"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
HostedDomain string `json:"hd"`
|
||||
}
|
||||
if err := idToken.Claims(&claims); err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
|
||||
}
|
||||
|
||||
if len(c.hostedDomains) > 0 {
|
||||
found := false
|
||||
for _, domain := range c.hostedDomains {
|
||||
if claims.HostedDomain == domain {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return identity, fmt.Errorf("oidc: unexpected hd claim %v", claims.HostedDomain)
|
||||
}
|
||||
}
|
||||
|
||||
var groups []string
|
||||
if s.Groups && c.adminEmail != "" && c.serviceAccountFilePath != "" {
|
||||
groups, err = c.getGroups(claims.Email)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("google: could not retrieve groups: %v", err)
|
||||
}
|
||||
|
||||
if len(c.groups) > 0 {
|
||||
groups = pkg_groups.Filter(groups, c.groups)
|
||||
if len(groups) == 0 {
|
||||
return identity, fmt.Errorf("google: user %q is not in any of the required groups", claims.Username)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identity = connector.Identity{
|
||||
UserID: idToken.Subject,
|
||||
Username: claims.Username,
|
||||
Email: claims.Email,
|
||||
EmailVerified: claims.EmailVerified,
|
||||
ConnectorData: []byte(token.RefreshToken),
|
||||
Groups: groups,
|
||||
}
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
// getGroups creates a connection to the admin directory service and lists
|
||||
// all groups the user is a member of
|
||||
func (c *googleConnector) getGroups(email string) ([]string, error) {
|
||||
groupsList, err := c.adminSrv.Groups.List().UserKey(email).Do()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not list groups: %v", err)
|
||||
}
|
||||
|
||||
var userGroups []string
|
||||
for _, group := range groupsList.Groups {
|
||||
// TODO (joelspeed): Make desried group key configurable
|
||||
userGroups = append(userGroups, group.Email)
|
||||
}
|
||||
|
||||
return userGroups, nil
|
||||
}
|
||||
|
||||
// createDirectoryService loads a google service account credentials file,
|
||||
// sets up super user impersonation and creates an admin client for calling
|
||||
// the google admin api
|
||||
func createDirectoryService(serviceAccountFilePath string, email string) (*admin.Service, error) {
|
||||
jsonCredentials, err := ioutil.ReadFile(serviceAccountFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading credentials from file: %v", err)
|
||||
}
|
||||
|
||||
config, err := google.JWTConfigFromJSON(jsonCredentials, admin.AdminDirectoryGroupReadonlyScope)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse client secret file to config: %v", err)
|
||||
}
|
||||
|
||||
// Impersonate an admin. This is mandatory for the admin APIs.
|
||||
config.Subject = email
|
||||
|
||||
ctx := context.Background()
|
||||
client := config.Client(ctx)
|
||||
|
||||
srv, err := admin.New(client)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create directory service %v", err)
|
||||
}
|
||||
return srv, nil
|
||||
}
|
|
@ -150,7 +150,6 @@ func (p *conn) Prompt() string { return "username" }
|
|||
|
||||
func (p *conn) Refresh(
|
||||
ctx context.Context, scopes connector.Scopes, identity connector.Identity) (connector.Identity, error) {
|
||||
|
||||
token, err := p.getAdminToken(ctx)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("keystone: failed to obtain admin token: %v", err)
|
||||
|
@ -210,6 +209,8 @@ func (p *conn) getAdminToken(ctx context.Context) (string, error) {
|
|||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
token := resp.Header.Get("X-Subject-Token")
|
||||
return token, nil
|
||||
}
|
||||
|
@ -229,6 +230,7 @@ func (p *conn) checkIfUserExists(ctx context.Context, userID string, token strin
|
|||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode == 200 {
|
||||
return true, nil
|
||||
|
|
|
@ -154,7 +154,12 @@ func delete(t *testing.T, token, id, uri string) {
|
|||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
req.Header.Set("X-Auth-Token", token)
|
||||
client.Do(req)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
}
|
||||
|
||||
func createGroup(t *testing.T, token, description, name string) string {
|
||||
|
@ -208,7 +213,13 @@ func addUserToGroup(t *testing.T, token, groupID, userID string) error {
|
|||
return err
|
||||
}
|
||||
req.Header.Set("X-Auth-Token", token)
|
||||
client.Do(req)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
t.Fatalf("error: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -274,7 +285,7 @@ func TestUseRefreshToken(t *testing.T) {
|
|||
delete(t, token, groupID, groupsURL)
|
||||
|
||||
expectEquals(t, 1, len(identityRefresh.Groups))
|
||||
expectEquals(t, testGroup, string(identityRefresh.Groups[0]))
|
||||
expectEquals(t, testGroup, identityRefresh.Groups[0])
|
||||
}
|
||||
|
||||
func TestUseRefreshTokenUserDeleted(t *testing.T) {
|
||||
|
|
|
@ -39,6 +39,7 @@ import (
|
|||
// idAttr: uid
|
||||
// emailAttr: mail
|
||||
// nameAttr: name
|
||||
// preferredUsernameAttr: uid
|
||||
// groupSearch:
|
||||
// # Would translate to the query "(&(objectClass=group)(member=<user uid>))"
|
||||
// baseDN: cn=groups,dc=example,dc=com
|
||||
|
@ -103,9 +104,10 @@ type Config struct {
|
|||
Scope string `json:"scope"`
|
||||
|
||||
// A mapping of attributes on the user entry to claims.
|
||||
IDAttr string `json:"idAttr"` // Defaults to "uid"
|
||||
EmailAttr string `json:"emailAttr"` // Defaults to "mail"
|
||||
NameAttr string `json:"nameAttr"` // No default.
|
||||
IDAttr string `json:"idAttr"` // Defaults to "uid"
|
||||
EmailAttr string `json:"emailAttr"` // Defaults to "mail"
|
||||
NameAttr string `json:"nameAttr"` // No default.
|
||||
PreferredUsernameAttrAttr string `json:"preferredUsernameAttr"` // No default.
|
||||
|
||||
// If this is set, the email claim of the id token will be constructed from the idAttr and
|
||||
// value of emailSuffix. This should not include the @ character.
|
||||
|
@ -187,7 +189,6 @@ func (c *Config) OpenConnector(logger log.Logger) (interface {
|
|||
}
|
||||
|
||||
func (c *Config) openConnector(logger log.Logger) (*ldapConnector, error) {
|
||||
|
||||
requiredFields := []struct {
|
||||
name string
|
||||
val string
|
||||
|
@ -341,6 +342,12 @@ func (c *ldapConnector) identityFromEntry(user ldap.Entry) (ident connector.Iden
|
|||
}
|
||||
}
|
||||
|
||||
if c.UserSearch.PreferredUsernameAttrAttr != "" {
|
||||
if ident.PreferredUsername = getAttr(user, c.UserSearch.PreferredUsernameAttrAttr); ident.PreferredUsername == "" {
|
||||
missing = append(missing, c.UserSearch.PreferredUsernameAttrAttr)
|
||||
}
|
||||
}
|
||||
|
||||
if c.UserSearch.EmailSuffix != "" {
|
||||
ident.Email = ident.Username + "@" + c.UserSearch.EmailSuffix
|
||||
} else if ident.Email = getAttr(user, c.UserSearch.EmailAttr); ident.Email == "" {
|
||||
|
@ -357,7 +364,6 @@ func (c *ldapConnector) identityFromEntry(user ldap.Entry) (ident connector.Iden
|
|||
}
|
||||
|
||||
func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.Entry, found bool, err error) {
|
||||
|
||||
filter := fmt.Sprintf("(%s=%s)", c.UserSearch.Username, ldap.EscapeFilter(username))
|
||||
if c.UserSearch.Filter != "" {
|
||||
filter = fmt.Sprintf("(&%s%s)", c.UserSearch.Filter, filter)
|
||||
|
@ -381,6 +387,10 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E
|
|||
req.Attributes = append(req.Attributes, c.UserSearch.NameAttr)
|
||||
}
|
||||
|
||||
if c.UserSearch.PreferredUsernameAttrAttr != "" {
|
||||
req.Attributes = append(req.Attributes, c.UserSearch.PreferredUsernameAttrAttr)
|
||||
}
|
||||
|
||||
c.logger.Infof("performing ldap search %s %s %s",
|
||||
req.BaseDN, scopeString(req.Scope), req.Filter)
|
||||
resp, err := conn.Search(req)
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
package ldap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/kylelemons/godebug/pretty"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/testcontainers/testcontainers-go"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
)
|
||||
|
@ -50,12 +48,6 @@ type subtest struct {
|
|||
|
||||
func TestQuery(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -125,12 +117,6 @@ userpassword: bar
|
|||
|
||||
func TestQueryWithEmailSuffix(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -187,12 +173,6 @@ userpassword: bar
|
|||
|
||||
func TestUserFilter(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=Seattle,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: Seattle
|
||||
|
@ -283,12 +263,6 @@ userpassword: bar
|
|||
|
||||
func TestGroupQuery(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -371,12 +345,6 @@ member: cn=jane,ou=People,dc=example,dc=org
|
|||
|
||||
func TestGroupsOnUserEntity(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -468,12 +436,6 @@ gidNumber: 1002
|
|||
|
||||
func TestGroupFilter(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -574,12 +536,6 @@ member: cn=jane,ou=People,dc=example,dc=org
|
|||
|
||||
func TestStartTLS(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -617,12 +573,6 @@ userpassword: foo
|
|||
|
||||
func TestInsecureSkipVerify(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -660,12 +610,6 @@ userpassword: foo
|
|||
|
||||
func TestLDAPS(t *testing.T) {
|
||||
schema := `
|
||||
dn: dc=example,dc=org
|
||||
objectClass: dcObject
|
||||
objectClass: organization
|
||||
o: Example Company
|
||||
dc: example
|
||||
|
||||
dn: ou=People,dc=example,dc=org
|
||||
objectClass: organizationalUnit
|
||||
ou: People
|
||||
|
@ -729,8 +673,7 @@ func TestUsernamePrompt(t *testing.T) {
|
|||
// runTests runs a set of tests against an LDAP schema. It does this by
|
||||
// setting up an OpenLDAP server and injecting the provided scheme.
|
||||
//
|
||||
// The tests require the slapd and ldapadd binaries available in the host
|
||||
// machine's PATH.
|
||||
// The tests require Docker.
|
||||
//
|
||||
// The DEX_LDAP_TESTS must be set to "1"
|
||||
func runTests(t *testing.T, schema string, connMethod connectionMethod, config *Config, tests []subtest) {
|
||||
|
@ -738,12 +681,6 @@ func runTests(t *testing.T, schema string, connMethod connectionMethod, config *
|
|||
t.Skipf("%s not set. Skipping test (run 'export %s=1' to run tests)", envVar, envVar)
|
||||
}
|
||||
|
||||
for _, cmd := range []string{"slapd", "ldapadd"} {
|
||||
if _, err := exec.LookPath(cmd); err != nil {
|
||||
t.Errorf("%s not available", cmd)
|
||||
}
|
||||
}
|
||||
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -755,100 +692,65 @@ func runTests(t *testing.T, schema string, connMethod connectionMethod, config *
|
|||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
configBytes := new(bytes.Buffer)
|
||||
|
||||
data := tmplData{
|
||||
TempDir: tempDir,
|
||||
Includes: includes(t, wd),
|
||||
}
|
||||
data.TLSCertPath, data.TLSKeyPath = tlsAssets(t, wd)
|
||||
|
||||
if err := slapdConfigTmpl.Execute(configBytes, data); err != nil {
|
||||
schemaPath := filepath.Join(tempDir, "schema.ldif")
|
||||
if err := ioutil.WriteFile(schemaPath, []byte(schema), 0777); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
configPath := filepath.Join(tempDir, "ldap.conf")
|
||||
if err := ioutil.WriteFile(configPath, configBytes.Bytes(), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
schemaPath := filepath.Join(tempDir, "schema.ldap")
|
||||
if err := ioutil.WriteFile(schemaPath, []byte(schema), 0644); err != nil {
|
||||
t.Fatal(err)
|
||||
req := testcontainers.ContainerRequest{
|
||||
Image: "osixia/openldap:1.3.0",
|
||||
ExposedPorts: []string{"389/tcp", "636/tcp"},
|
||||
Cmd: []string{"--copy-service"},
|
||||
Env: map[string]string{
|
||||
"LDAP_BASE_DN": "dc=example,dc=org",
|
||||
"LDAP_TLS": "true",
|
||||
"LDAP_TLS_VERIFY_CLIENT": "try",
|
||||
},
|
||||
BindMounts: map[string]string{
|
||||
filepath.Join(wd, "testdata", "certs"): "/container/service/slapd/assets/certs",
|
||||
schemaPath: "/container/service/slapd/assets/config/bootstrap/ldif/99-schema.ldif",
|
||||
},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.ForLog("slapd starting").WithOccurrence(3).WithStartupTimeout(time.Minute),
|
||||
wait.ForListeningPort("389/tcp"),
|
||||
wait.ForListeningPort("636/tcp"),
|
||||
),
|
||||
}
|
||||
|
||||
socketPath := url.QueryEscape(filepath.Join(tempDir, "ldap.unix"))
|
||||
ctx := context.Background()
|
||||
|
||||
slapdOut := new(bytes.Buffer)
|
||||
slapd, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
|
||||
ContainerRequest: req,
|
||||
Started: true,
|
||||
})
|
||||
if err != nil {
|
||||
if slapd != nil {
|
||||
logs, err := slapd.Logs(ctx)
|
||||
if err == nil {
|
||||
defer logs.Close()
|
||||
|
||||
cmd := exec.Command(
|
||||
"slapd",
|
||||
"-d", "any",
|
||||
"-h", "ldap://localhost:10389/ ldaps://localhost:10636/ ldapi://"+socketPath,
|
||||
"-f", configPath,
|
||||
)
|
||||
cmd.Stdout = slapdOut
|
||||
cmd.Stderr = slapdOut
|
||||
if err := cmd.Start(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var (
|
||||
// Wait group finishes once slapd has exited.
|
||||
//
|
||||
// Use a wait group because multiple goroutines can't listen on
|
||||
// cmd.Wait(). It triggers the race detector.
|
||||
wg = new(sync.WaitGroup)
|
||||
// Ensure only one condition can set the slapdFailed boolean.
|
||||
once = new(sync.Once)
|
||||
slapdFailed bool
|
||||
)
|
||||
|
||||
wg.Add(1)
|
||||
go func() { cmd.Wait(); wg.Done() }()
|
||||
|
||||
defer func() {
|
||||
if slapdFailed {
|
||||
// If slapd exited before it was killed, print its logs.
|
||||
t.Logf("%s\n", slapdOut)
|
||||
logLines, err := ioutil.ReadAll(logs)
|
||||
if err != nil {
|
||||
t.Log(string(logLines))
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
wg.Wait()
|
||||
once.Do(func() { slapdFailed = true })
|
||||
}()
|
||||
|
||||
defer func() {
|
||||
once.Do(func() { slapdFailed = false })
|
||||
cmd.Process.Kill()
|
||||
wg.Wait()
|
||||
}()
|
||||
|
||||
// Try a few times to connect to the LDAP server. On slower machines
|
||||
// it can take a while for it to come up.
|
||||
connected := false
|
||||
wait := 100 * time.Millisecond
|
||||
for i := 0; i < 5; i++ {
|
||||
time.Sleep(wait)
|
||||
|
||||
ldapadd := exec.Command(
|
||||
"ldapadd", "-x",
|
||||
"-D", "cn=admin,dc=example,dc=org",
|
||||
"-w", "admin",
|
||||
"-f", schemaPath,
|
||||
"-H", "ldap://localhost:10389/",
|
||||
)
|
||||
if out, err := ldapadd.CombinedOutput(); err != nil {
|
||||
t.Logf("ldapadd: %s", out)
|
||||
wait = wait * 2 // backoff
|
||||
continue
|
||||
}
|
||||
connected = true
|
||||
break
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !connected {
|
||||
t.Errorf("ldapadd command failed")
|
||||
return
|
||||
defer slapd.Terminate(ctx)
|
||||
|
||||
ip, err := slapd.Host(ctx)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
port, err := slapd.MappedPort(ctx, "389")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
tlsPort, err := slapd.MappedPort(ctx, "636")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Shallow copy.
|
||||
|
@ -858,17 +760,17 @@ func runTests(t *testing.T, schema string, connMethod connectionMethod, config *
|
|||
// group search configuration.
|
||||
switch connMethod {
|
||||
case connectStartTLS:
|
||||
c.Host = "localhost:10389"
|
||||
c.RootCA = "testdata/ca.crt"
|
||||
c.Host = fmt.Sprintf("%s:%s", ip, port.Port())
|
||||
c.RootCA = "testdata/certs/ca.crt"
|
||||
c.StartTLS = true
|
||||
case connectLDAPS:
|
||||
c.Host = "localhost:10636"
|
||||
c.RootCA = "testdata/ca.crt"
|
||||
c.Host = fmt.Sprintf("%s:%s", ip, tlsPort.Port())
|
||||
c.RootCA = "testdata/certs/ca.crt"
|
||||
case connectInsecureSkipVerify:
|
||||
c.Host = "localhost:10636"
|
||||
c.Host = fmt.Sprintf("%s:%s", ip, tlsPort.Port())
|
||||
c.InsecureSkipVerify = true
|
||||
case connectLDAP:
|
||||
c.Host = "localhost:10389"
|
||||
c.Host = fmt.Sprintf("%s:%s", ip, port.Port())
|
||||
c.InsecureNoSSL = true
|
||||
}
|
||||
|
||||
|
@ -934,98 +836,3 @@ func runTests(t *testing.T, schema string, connMethod connectionMethod, config *
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Standard OpenLDAP schema files to include.
|
||||
//
|
||||
// These are copied from the /etc/openldap/schema directory.
|
||||
var includeFiles = []string{
|
||||
"core.schema",
|
||||
"cosine.schema",
|
||||
"inetorgperson.schema",
|
||||
"misc.schema",
|
||||
"nis.schema",
|
||||
"openldap.schema",
|
||||
}
|
||||
|
||||
// tmplData is the struct used to execute the SLAPD config template.
|
||||
type tmplData struct {
|
||||
// Directory for database to be writen to.
|
||||
TempDir string
|
||||
// List of schema files to include.
|
||||
Includes []string
|
||||
// TLS assets for LDAPS.
|
||||
TLSKeyPath string
|
||||
TLSCertPath string
|
||||
}
|
||||
|
||||
// Config template copied from:
|
||||
// http://www.zytrax.com/books/ldap/ch5/index.html#step1-slapd
|
||||
//
|
||||
// TLS instructions found here:
|
||||
// http://www.openldap.org/doc/admin24/tls.html
|
||||
var slapdConfigTmpl = template.Must(template.New("").Parse(`
|
||||
{{ range $i, $include := .Includes }}
|
||||
include {{ $include }}
|
||||
{{ end }}
|
||||
|
||||
# MODULELOAD definitions
|
||||
# not required (comment out) before version 2.3
|
||||
moduleload back_bdb.la
|
||||
|
||||
database bdb
|
||||
suffix "dc=example,dc=org"
|
||||
|
||||
# root or superuser
|
||||
rootdn "cn=admin,dc=example,dc=org"
|
||||
rootpw admin
|
||||
# The database directory MUST exist prior to running slapd AND
|
||||
# change path as necessary
|
||||
directory {{ .TempDir }}
|
||||
|
||||
TLSCertificateFile {{ .TLSCertPath }}
|
||||
TLSCertificateKeyFile {{ .TLSKeyPath }}
|
||||
|
||||
# Indices to maintain for this directory
|
||||
# unique id so equality match only
|
||||
index uid eq
|
||||
# allows general searching on commonname, givenname and email
|
||||
index cn,gn,mail eq,sub
|
||||
# allows multiple variants on surname searching
|
||||
index sn eq,sub
|
||||
# sub above includes subintial,subany,subfinal
|
||||
# optimise department searches
|
||||
index ou eq
|
||||
# if searches will include objectClass uncomment following
|
||||
# index objectClass eq
|
||||
# shows use of default index parameter
|
||||
index default eq,sub
|
||||
# indices missing - uses default eq,sub
|
||||
index telephonenumber
|
||||
|
||||
# other database parameters
|
||||
# read more in slapd.conf reference section
|
||||
cachesize 10000
|
||||
checkpoint 128 15
|
||||
`))
|
||||
|
||||
func tlsAssets(t *testing.T, wd string) (certPath, keyPath string) {
|
||||
certPath = filepath.Join(wd, "testdata", "server.crt")
|
||||
keyPath = filepath.Join(wd, "testdata", "server.key")
|
||||
for _, p := range []string{certPath, keyPath} {
|
||||
if _, err := os.Stat(p); err != nil {
|
||||
t.Fatalf("failed to find TLS asset file: %s %v", p, err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func includes(t *testing.T, wd string) (paths []string) {
|
||||
for _, f := range includeFiles {
|
||||
p := filepath.Join(wd, "testdata", f)
|
||||
if _, err := os.Stat(p); err != nil {
|
||||
t.Fatalf("failed to find schema file: %s %v", p, err)
|
||||
}
|
||||
paths = append(paths, p)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
|
8
connector/ldap/testdata/certs/dhparam.pem
vendored
Normal file
8
connector/ldap/testdata/certs/dhparam.pem
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
-----BEGIN DH PARAMETERS-----
|
||||
MIIBCAKCAQEAx5y2viJKOAAcDYSj55odZsbA7dkSQ9afEPd9uaCLOvRYKLJY1S1V
|
||||
C4m1eVfna8JndSLdsBGDQe4BlBTkEYMYR8CJHtUuBxeAucOH8KlF8rIHXXi71oex
|
||||
T7kPtJEDINQKOn06bHqNcn0a7ZMWP8jiQ708OYr5P+1T/N82QTAFpDuqK42ZnBqf
|
||||
8qzQkkTN0UCktY2EWnFTbNIXcMKWQnYP8zt/CG3Q31b2bnQt2iLEa/DIF7RLNjfx
|
||||
9wPQBBAqgWbLmWfdPpHsAPtQxtItb+GRbPs3aLm06CFKlQuteDoP+suo0EtglHcV
|
||||
V9Ynvdz0cdJCJ7EPyET6CtLMzc/Puup/AwIBAg==
|
||||
-----END DH PARAMETERS-----
|
610
connector/ldap/testdata/core.schema
vendored
610
connector/ldap/testdata/core.schema
vendored
|
@ -1,610 +0,0 @@
|
|||
# OpenLDAP Core schema
|
||||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 1998-2016 The OpenLDAP Foundation.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
#
|
||||
## Portions Copyright (C) The Internet Society (1997-2006).
|
||||
## All Rights Reserved.
|
||||
##
|
||||
## This document and translations of it may be copied and furnished to
|
||||
## others, and derivative works that comment on or otherwise explain it
|
||||
## or assist in its implementation may be prepared, copied, published
|
||||
## and distributed, in whole or in part, without restriction of any
|
||||
## kind, provided that the above copyright notice and this paragraph are
|
||||
## included on all such copies and derivative works. However, this
|
||||
## document itself may not be modified in any way, such as by removing
|
||||
## the copyright notice or references to the Internet Society or other
|
||||
## Internet organizations, except as needed for the purpose of
|
||||
## developing Internet standards in which case the procedures for
|
||||
## copyrights defined in the Internet Standards process must be
|
||||
## followed, or as required to translate it into languages other than
|
||||
## English.
|
||||
##
|
||||
## The limited permissions granted above are perpetual and will not be
|
||||
## revoked by the Internet Society or its successors or assigns.
|
||||
##
|
||||
## This document and the information contained herein is provided on an
|
||||
## "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
|
||||
## TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
||||
## BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
|
||||
## HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
|
||||
## MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
#
|
||||
#
|
||||
# Includes LDAPv3 schema items from:
|
||||
# RFC 2252/2256 (LDAPv3)
|
||||
#
|
||||
# Select standard track schema items:
|
||||
# RFC 1274 (uid/dc)
|
||||
# RFC 2079 (URI)
|
||||
# RFC 2247 (dc/dcObject)
|
||||
# RFC 2587 (PKI)
|
||||
# RFC 2589 (Dynamic Directory Services)
|
||||
# RFC 4524 (associatedDomain)
|
||||
#
|
||||
# Select informational schema items:
|
||||
# RFC 2377 (uidObject)
|
||||
|
||||
#
|
||||
# Standard attribute types from RFC 2256
|
||||
#
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.0 NAME 'objectClass'
|
||||
# DESC 'RFC2256: object classes of the entity'
|
||||
# EQUALITY objectIdentifierMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.1 NAME ( 'aliasedObjectName' 'aliasedEntryName' )
|
||||
# DESC 'RFC2256: name of aliased object'
|
||||
# EQUALITY distinguishedNameMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 2.5.4.2 NAME 'knowledgeInformation'
|
||||
DESC 'RFC2256: knowledge information'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.3 NAME ( 'cn' 'commonName' )
|
||||
# DESC 'RFC2256: common name(s) for which the entity is known by'
|
||||
# SUP name )
|
||||
|
||||
attributetype ( 2.5.4.4 NAME ( 'sn' 'surname' )
|
||||
DESC 'RFC2256: last (family) name(s) for which the entity is known by'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.5 NAME 'serialNumber'
|
||||
DESC 'RFC2256: serial number of the entity'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{64} )
|
||||
|
||||
# RFC 4519 definition ('countryName' in X.500 and RFC2256)
|
||||
attributetype ( 2.5.4.6 NAME ( 'c' 'countryName' )
|
||||
DESC 'RFC4519: two-letter ISO-3166 country code'
|
||||
SUP name
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.11
|
||||
SINGLE-VALUE )
|
||||
|
||||
#attributetype ( 2.5.4.6 NAME ( 'c' 'countryName' )
|
||||
# DESC 'RFC2256: ISO-3166 country 2-letter code'
|
||||
# SUP name SINGLE-VALUE )
|
||||
|
||||
attributetype ( 2.5.4.7 NAME ( 'l' 'localityName' )
|
||||
DESC 'RFC2256: locality which this object resides in'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.8 NAME ( 'st' 'stateOrProvinceName' )
|
||||
DESC 'RFC2256: state or province which this object resides in'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.9 NAME ( 'street' 'streetAddress' )
|
||||
DESC 'RFC2256: street address of this object'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
|
||||
attributetype ( 2.5.4.10 NAME ( 'o' 'organizationName' )
|
||||
DESC 'RFC2256: organization this object belongs to'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.11 NAME ( 'ou' 'organizationalUnitName' )
|
||||
DESC 'RFC2256: organizational unit this object belongs to'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.12 NAME 'title'
|
||||
DESC 'RFC2256: title associated with the entity'
|
||||
SUP name )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.13 NAME 'description'
|
||||
# DESC 'RFC2256: descriptive information'
|
||||
# EQUALITY caseIgnoreMatch
|
||||
# SUBSTR caseIgnoreSubstringsMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{1024} )
|
||||
|
||||
# Deprecated by enhancedSearchGuide
|
||||
attributetype ( 2.5.4.14 NAME 'searchGuide'
|
||||
DESC 'RFC2256: search guide, deprecated by enhancedSearchGuide'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.25 )
|
||||
|
||||
attributetype ( 2.5.4.15 NAME 'businessCategory'
|
||||
DESC 'RFC2256: business category'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
|
||||
attributetype ( 2.5.4.16 NAME 'postalAddress'
|
||||
DESC 'RFC2256: postal address'
|
||||
EQUALITY caseIgnoreListMatch
|
||||
SUBSTR caseIgnoreListSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
|
||||
attributetype ( 2.5.4.17 NAME 'postalCode'
|
||||
DESC 'RFC2256: postal code'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} )
|
||||
|
||||
attributetype ( 2.5.4.18 NAME 'postOfficeBox'
|
||||
DESC 'RFC2256: Post Office Box'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{40} )
|
||||
|
||||
attributetype ( 2.5.4.19 NAME 'physicalDeliveryOfficeName'
|
||||
DESC 'RFC2256: Physical Delivery Office Name'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{128} )
|
||||
|
||||
attributetype ( 2.5.4.20 NAME 'telephoneNumber'
|
||||
DESC 'RFC2256: Telephone Number'
|
||||
EQUALITY telephoneNumberMatch
|
||||
SUBSTR telephoneNumberSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.50{32} )
|
||||
|
||||
attributetype ( 2.5.4.21 NAME 'telexNumber'
|
||||
DESC 'RFC2256: Telex Number'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.52 )
|
||||
|
||||
attributetype ( 2.5.4.22 NAME 'teletexTerminalIdentifier'
|
||||
DESC 'RFC2256: Teletex Terminal Identifier'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.51 )
|
||||
|
||||
attributetype ( 2.5.4.23 NAME ( 'facsimileTelephoneNumber' 'fax' )
|
||||
DESC 'RFC2256: Facsimile (Fax) Telephone Number'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.22 )
|
||||
|
||||
attributetype ( 2.5.4.24 NAME 'x121Address'
|
||||
DESC 'RFC2256: X.121 Address'
|
||||
EQUALITY numericStringMatch
|
||||
SUBSTR numericStringSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{15} )
|
||||
|
||||
attributetype ( 2.5.4.25 NAME 'internationaliSDNNumber'
|
||||
DESC 'RFC2256: international ISDN number'
|
||||
EQUALITY numericStringMatch
|
||||
SUBSTR numericStringSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.36{16} )
|
||||
|
||||
attributetype ( 2.5.4.26 NAME 'registeredAddress'
|
||||
DESC 'RFC2256: registered postal address'
|
||||
SUP postalAddress
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.41 )
|
||||
|
||||
attributetype ( 2.5.4.27 NAME 'destinationIndicator'
|
||||
DESC 'RFC2256: destination indicator'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.44{128} )
|
||||
|
||||
attributetype ( 2.5.4.28 NAME 'preferredDeliveryMethod'
|
||||
DESC 'RFC2256: preferred delivery method'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.14
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype ( 2.5.4.29 NAME 'presentationAddress'
|
||||
DESC 'RFC2256: presentation address'
|
||||
EQUALITY presentationAddressMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.43
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype ( 2.5.4.30 NAME 'supportedApplicationContext'
|
||||
DESC 'RFC2256: supported application context'
|
||||
EQUALITY objectIdentifierMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 )
|
||||
|
||||
attributetype ( 2.5.4.31 NAME 'member'
|
||||
DESC 'RFC2256: member of a group'
|
||||
SUP distinguishedName )
|
||||
|
||||
attributetype ( 2.5.4.32 NAME 'owner'
|
||||
DESC 'RFC2256: owner (of the object)'
|
||||
SUP distinguishedName )
|
||||
|
||||
attributetype ( 2.5.4.33 NAME 'roleOccupant'
|
||||
DESC 'RFC2256: occupant of role'
|
||||
SUP distinguishedName )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.34 NAME 'seeAlso'
|
||||
# DESC 'RFC2256: DN of related object'
|
||||
# SUP distinguishedName )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.35 NAME 'userPassword'
|
||||
# DESC 'RFC2256/2307: password of user'
|
||||
# EQUALITY octetStringMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128} )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
# with certificateExactMatch rule (per X.509)
|
||||
attributetype ( 2.5.4.36 NAME 'userCertificate'
|
||||
DESC 'RFC2256: X.509 user certificate, use ;binary'
|
||||
EQUALITY certificateExactMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
# with certificateExactMatch rule (per X.509)
|
||||
attributetype ( 2.5.4.37 NAME 'cACertificate'
|
||||
DESC 'RFC2256: X.509 CA certificate, use ;binary'
|
||||
EQUALITY certificateExactMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.8 )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
attributetype ( 2.5.4.38 NAME 'authorityRevocationList'
|
||||
DESC 'RFC2256: X.509 authority revocation list, use ;binary'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
attributetype ( 2.5.4.39 NAME 'certificateRevocationList'
|
||||
DESC 'RFC2256: X.509 certificate revocation list, use ;binary'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
|
||||
|
||||
# Must be stored and requested in the binary form
|
||||
attributetype ( 2.5.4.40 NAME 'crossCertificatePair'
|
||||
DESC 'RFC2256: X.509 cross certificate pair, use ;binary'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.10 )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.41 NAME 'name'
|
||||
# EQUALITY caseIgnoreMatch
|
||||
# SUBSTR caseIgnoreSubstringsMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
|
||||
|
||||
attributetype ( 2.5.4.42 NAME ( 'givenName' 'gn' )
|
||||
DESC 'RFC2256: first name(s) for which the entity is known by'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.43 NAME 'initials'
|
||||
DESC 'RFC2256: initials of some or all of names, but not the surname(s).'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.44 NAME 'generationQualifier'
|
||||
DESC 'RFC2256: name qualifier indicating a generation'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.45 NAME 'x500UniqueIdentifier'
|
||||
DESC 'RFC2256: X.500 unique identifier'
|
||||
EQUALITY bitStringMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.6 )
|
||||
|
||||
attributetype ( 2.5.4.46 NAME 'dnQualifier'
|
||||
DESC 'RFC2256: DN qualifier'
|
||||
EQUALITY caseIgnoreMatch
|
||||
ORDERING caseIgnoreOrderingMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.44 )
|
||||
|
||||
attributetype ( 2.5.4.47 NAME 'enhancedSearchGuide'
|
||||
DESC 'RFC2256: enhanced search guide'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.21 )
|
||||
|
||||
attributetype ( 2.5.4.48 NAME 'protocolInformation'
|
||||
DESC 'RFC2256: protocol information'
|
||||
EQUALITY protocolInformationMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.42 )
|
||||
|
||||
# system schema
|
||||
#attributetype ( 2.5.4.49 NAME 'distinguishedName'
|
||||
# EQUALITY distinguishedNameMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 )
|
||||
|
||||
attributetype ( 2.5.4.50 NAME 'uniqueMember'
|
||||
DESC 'RFC2256: unique member of a group'
|
||||
EQUALITY uniqueMemberMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.34 )
|
||||
|
||||
attributetype ( 2.5.4.51 NAME 'houseIdentifier'
|
||||
DESC 'RFC2256: house identifier'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
attributetype ( 2.5.4.52 NAME 'supportedAlgorithms'
|
||||
DESC 'RFC2256: supported algorithms'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.49 )
|
||||
|
||||
# Must be transferred using ;binary
|
||||
attributetype ( 2.5.4.53 NAME 'deltaRevocationList'
|
||||
DESC 'RFC2256: delta revocation list; use ;binary'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.9 )
|
||||
|
||||
attributetype ( 2.5.4.54 NAME 'dmdName'
|
||||
DESC 'RFC2256: name of DMD'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 2.5.4.65 NAME 'pseudonym'
|
||||
DESC 'X.520(4th): pseudonym for the object'
|
||||
SUP name )
|
||||
|
||||
# Standard object classes from RFC2256
|
||||
|
||||
# system schema
|
||||
#objectclass ( 2.5.6.0 NAME 'top'
|
||||
# DESC 'RFC2256: top of the superclass chain'
|
||||
# ABSTRACT
|
||||
# MUST objectClass )
|
||||
|
||||
# system schema
|
||||
#objectclass ( 2.5.6.1 NAME 'alias'
|
||||
# DESC 'RFC2256: an alias'
|
||||
# SUP top STRUCTURAL
|
||||
# MUST aliasedObjectName )
|
||||
|
||||
objectclass ( 2.5.6.2 NAME 'country'
|
||||
DESC 'RFC2256: a country'
|
||||
SUP top STRUCTURAL
|
||||
MUST c
|
||||
MAY ( searchGuide $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.3 NAME 'locality'
|
||||
DESC 'RFC2256: a locality'
|
||||
SUP top STRUCTURAL
|
||||
MAY ( street $ seeAlso $ searchGuide $ st $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.4 NAME 'organization'
|
||||
DESC 'RFC2256: an organization'
|
||||
SUP top STRUCTURAL
|
||||
MUST o
|
||||
MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
|
||||
x121Address $ registeredAddress $ destinationIndicator $
|
||||
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
|
||||
telephoneNumber $ internationaliSDNNumber $
|
||||
facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
|
||||
postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.5 NAME 'organizationalUnit'
|
||||
DESC 'RFC2256: an organizational unit'
|
||||
SUP top STRUCTURAL
|
||||
MUST ou
|
||||
MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
|
||||
x121Address $ registeredAddress $ destinationIndicator $
|
||||
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
|
||||
telephoneNumber $ internationaliSDNNumber $
|
||||
facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
|
||||
postalAddress $ physicalDeliveryOfficeName $ st $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.6 NAME 'person'
|
||||
DESC 'RFC2256: a person'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( sn $ cn )
|
||||
MAY ( userPassword $ telephoneNumber $ seeAlso $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.7 NAME 'organizationalPerson'
|
||||
DESC 'RFC2256: an organizational person'
|
||||
SUP person STRUCTURAL
|
||||
MAY ( title $ x121Address $ registeredAddress $ destinationIndicator $
|
||||
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
|
||||
telephoneNumber $ internationaliSDNNumber $
|
||||
facsimileTelephoneNumber $ street $ postOfficeBox $ postalCode $
|
||||
postalAddress $ physicalDeliveryOfficeName $ ou $ st $ l ) )
|
||||
|
||||
objectclass ( 2.5.6.8 NAME 'organizationalRole'
|
||||
DESC 'RFC2256: an organizational role'
|
||||
SUP top STRUCTURAL
|
||||
MUST cn
|
||||
MAY ( x121Address $ registeredAddress $ destinationIndicator $
|
||||
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
|
||||
telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $
|
||||
seeAlso $ roleOccupant $ preferredDeliveryMethod $ street $
|
||||
postOfficeBox $ postalCode $ postalAddress $
|
||||
physicalDeliveryOfficeName $ ou $ st $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.9 NAME 'groupOfNames'
|
||||
DESC 'RFC2256: a group of names (DNs)'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( member $ cn )
|
||||
MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.10 NAME 'residentialPerson'
|
||||
DESC 'RFC2256: an residential person'
|
||||
SUP person STRUCTURAL
|
||||
MUST l
|
||||
MAY ( businessCategory $ x121Address $ registeredAddress $
|
||||
destinationIndicator $ preferredDeliveryMethod $ telexNumber $
|
||||
teletexTerminalIdentifier $ telephoneNumber $ internationaliSDNNumber $
|
||||
facsimileTelephoneNumber $ preferredDeliveryMethod $ street $
|
||||
postOfficeBox $ postalCode $ postalAddress $
|
||||
physicalDeliveryOfficeName $ st $ l ) )
|
||||
|
||||
objectclass ( 2.5.6.11 NAME 'applicationProcess'
|
||||
DESC 'RFC2256: an application process'
|
||||
SUP top STRUCTURAL
|
||||
MUST cn
|
||||
MAY ( seeAlso $ ou $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.12 NAME 'applicationEntity'
|
||||
DESC 'RFC2256: an application entity'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( presentationAddress $ cn )
|
||||
MAY ( supportedApplicationContext $ seeAlso $ ou $ o $ l $
|
||||
description ) )
|
||||
|
||||
objectclass ( 2.5.6.13 NAME 'dSA'
|
||||
DESC 'RFC2256: a directory system agent (a server)'
|
||||
SUP applicationEntity STRUCTURAL
|
||||
MAY knowledgeInformation )
|
||||
|
||||
objectclass ( 2.5.6.14 NAME 'device'
|
||||
DESC 'RFC2256: a device'
|
||||
SUP top STRUCTURAL
|
||||
MUST cn
|
||||
MAY ( serialNumber $ seeAlso $ owner $ ou $ o $ l $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.15 NAME 'strongAuthenticationUser'
|
||||
DESC 'RFC2256: a strong authentication user'
|
||||
SUP top AUXILIARY
|
||||
MUST userCertificate )
|
||||
|
||||
objectclass ( 2.5.6.16 NAME 'certificationAuthority'
|
||||
DESC 'RFC2256: a certificate authority'
|
||||
SUP top AUXILIARY
|
||||
MUST ( authorityRevocationList $ certificateRevocationList $
|
||||
cACertificate ) MAY crossCertificatePair )
|
||||
|
||||
objectclass ( 2.5.6.17 NAME 'groupOfUniqueNames'
|
||||
DESC 'RFC2256: a group of unique names (DN and Unique Identifier)'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( uniqueMember $ cn )
|
||||
MAY ( businessCategory $ seeAlso $ owner $ ou $ o $ description ) )
|
||||
|
||||
objectclass ( 2.5.6.18 NAME 'userSecurityInformation'
|
||||
DESC 'RFC2256: a user security information'
|
||||
SUP top AUXILIARY
|
||||
MAY ( supportedAlgorithms ) )
|
||||
|
||||
objectclass ( 2.5.6.16.2 NAME 'certificationAuthority-V2'
|
||||
SUP certificationAuthority
|
||||
AUXILIARY MAY ( deltaRevocationList ) )
|
||||
|
||||
objectclass ( 2.5.6.19 NAME 'cRLDistributionPoint'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn )
|
||||
MAY ( certificateRevocationList $ authorityRevocationList $
|
||||
deltaRevocationList ) )
|
||||
|
||||
objectclass ( 2.5.6.20 NAME 'dmd'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( dmdName )
|
||||
MAY ( userPassword $ searchGuide $ seeAlso $ businessCategory $
|
||||
x121Address $ registeredAddress $ destinationIndicator $
|
||||
preferredDeliveryMethod $ telexNumber $ teletexTerminalIdentifier $
|
||||
telephoneNumber $ internationaliSDNNumber $ facsimileTelephoneNumber $
|
||||
street $ postOfficeBox $ postalCode $ postalAddress $
|
||||
physicalDeliveryOfficeName $ st $ l $ description ) )
|
||||
|
||||
#
|
||||
# Object Classes from RFC 2587
|
||||
#
|
||||
objectclass ( 2.5.6.21 NAME 'pkiUser'
|
||||
DESC 'RFC2587: a PKI user'
|
||||
SUP top AUXILIARY
|
||||
MAY userCertificate )
|
||||
|
||||
objectclass ( 2.5.6.22 NAME 'pkiCA'
|
||||
DESC 'RFC2587: PKI certificate authority'
|
||||
SUP top AUXILIARY
|
||||
MAY ( authorityRevocationList $ certificateRevocationList $
|
||||
cACertificate $ crossCertificatePair ) )
|
||||
|
||||
objectclass ( 2.5.6.23 NAME 'deltaCRL'
|
||||
DESC 'RFC2587: PKI user'
|
||||
SUP top AUXILIARY
|
||||
MAY deltaRevocationList )
|
||||
|
||||
#
|
||||
# Standard Track URI label schema from RFC 2079
|
||||
# system schema
|
||||
#attributetype ( 1.3.6.1.4.1.250.1.57 NAME 'labeledURI'
|
||||
# DESC 'RFC2079: Uniform Resource Identifier with optional label'
|
||||
# EQUALITY caseExactMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
|
||||
objectclass ( 1.3.6.1.4.1.250.3.15 NAME 'labeledURIObject'
|
||||
DESC 'RFC2079: object that contains the URI attribute type'
|
||||
SUP top AUXILIARY
|
||||
MAY ( labeledURI ) )
|
||||
|
||||
#
|
||||
# Derived from RFC 1274, but with new "short names"
|
||||
#
|
||||
#attributetype ( 0.9.2342.19200300.100.1.1
|
||||
# NAME ( 'uid' 'userid' )
|
||||
# DESC 'RFC1274: user identifier'
|
||||
# EQUALITY caseIgnoreMatch
|
||||
# SUBSTR caseIgnoreSubstringsMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} )
|
||||
|
||||
attributetype ( 0.9.2342.19200300.100.1.3
|
||||
NAME ( 'mail' 'rfc822Mailbox' )
|
||||
DESC 'RFC1274: RFC822 Mailbox'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SUBSTR caseIgnoreIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
|
||||
objectclass ( 0.9.2342.19200300.100.4.19 NAME 'simpleSecurityObject'
|
||||
DESC 'RFC1274: simple security object'
|
||||
SUP top AUXILIARY
|
||||
MUST userPassword )
|
||||
|
||||
# RFC 1274 + RFC 2247
|
||||
attributetype ( 0.9.2342.19200300.100.1.25
|
||||
NAME ( 'dc' 'domainComponent' )
|
||||
DESC 'RFC1274/2247: domain component'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SUBSTR caseIgnoreIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
|
||||
# RFC 2247
|
||||
objectclass ( 1.3.6.1.4.1.1466.344 NAME 'dcObject'
|
||||
DESC 'RFC2247: domain component object'
|
||||
SUP top AUXILIARY MUST dc )
|
||||
|
||||
# RFC 2377
|
||||
objectclass ( 1.3.6.1.1.3.1 NAME 'uidObject'
|
||||
DESC 'RFC2377: uid object'
|
||||
SUP top AUXILIARY MUST uid )
|
||||
|
||||
# RFC 4524
|
||||
# The 'associatedDomain' attribute specifies DNS [RFC1034][RFC2181]
|
||||
# host names [RFC1123] that are associated with an object. That is,
|
||||
# values of this attribute should conform to the following ABNF:
|
||||
#
|
||||
# domain = root / label *( DOT label )
|
||||
# root = SPACE
|
||||
# label = LETDIG [ *61( LETDIG / HYPHEN ) LETDIG ]
|
||||
# LETDIG = %x30-39 / %x41-5A / %x61-7A ; "0" - "9" / "A"-"Z" / "a"-"z"
|
||||
# SPACE = %x20 ; space (" ")
|
||||
# HYPHEN = %x2D ; hyphen ("-")
|
||||
# DOT = %x2E ; period (".")
|
||||
attributetype ( 0.9.2342.19200300.100.1.37
|
||||
NAME 'associatedDomain'
|
||||
DESC 'RFC1274: domain associated with object'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SUBSTR caseIgnoreIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
|
||||
# RFC 2459 -- deprecated in favor of 'mail' (in cosine.schema)
|
||||
attributetype ( 1.2.840.113549.1.9.1
|
||||
NAME ( 'email' 'emailAddress' 'pkcs9email' )
|
||||
DESC 'RFC3280: legacy attribute for email addresses in DNs'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SUBSTR caseIgnoreIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
|
2571
connector/ldap/testdata/cosine.schema
vendored
2571
connector/ldap/testdata/cosine.schema
vendored
File diff suppressed because it is too large
Load diff
155
connector/ldap/testdata/inetorgperson.schema
vendored
155
connector/ldap/testdata/inetorgperson.schema
vendored
|
@ -1,155 +0,0 @@
|
|||
# inetorgperson.schema -- InetOrgPerson (RFC2798)
|
||||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 1998-2016 The OpenLDAP Foundation.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
#
|
||||
# InetOrgPerson (RFC2798)
|
||||
#
|
||||
# Depends upon
|
||||
# Definition of an X.500 Attribute Type and an Object Class to Hold
|
||||
# Uniform Resource Identifiers (URIs) [RFC2079]
|
||||
# (core.schema)
|
||||
#
|
||||
# A Summary of the X.500(96) User Schema for use with LDAPv3 [RFC2256]
|
||||
# (core.schema)
|
||||
#
|
||||
# The COSINE and Internet X.500 Schema [RFC1274] (cosine.schema)
|
||||
|
||||
# carLicense
|
||||
# This multivalued field is used to record the values of the license or
|
||||
# registration plate associated with an individual.
|
||||
attributetype ( 2.16.840.1.113730.3.1.1
|
||||
NAME 'carLicense'
|
||||
DESC 'RFC2798: vehicle license or registration plate'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
|
||||
# departmentNumber
|
||||
# Code for department to which a person belongs. This can also be
|
||||
# strictly numeric (e.g., 1234) or alphanumeric (e.g., ABC/123).
|
||||
attributetype ( 2.16.840.1.113730.3.1.2
|
||||
NAME 'departmentNumber'
|
||||
DESC 'RFC2798: identifies a department within an organization'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
|
||||
# displayName
|
||||
# When displaying an entry, especially within a one-line summary list, it
|
||||
# is useful to be able to identify a name to be used. Since other attri-
|
||||
# bute types such as 'cn' are multivalued, an additional attribute type is
|
||||
# needed. Display name is defined for this purpose.
|
||||
attributetype ( 2.16.840.1.113730.3.1.241
|
||||
NAME 'displayName'
|
||||
DESC 'RFC2798: preferred name to be used when displaying entries'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
|
||||
# employeeNumber
|
||||
# Numeric or alphanumeric identifier assigned to a person, typically based
|
||||
# on order of hire or association with an organization. Single valued.
|
||||
attributetype ( 2.16.840.1.113730.3.1.3
|
||||
NAME 'employeeNumber'
|
||||
DESC 'RFC2798: numerically identifies an employee within an organization'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
|
||||
# employeeType
|
||||
# Used to identify the employer to employee relationship. Typical values
|
||||
# used will be "Contractor", "Employee", "Intern", "Temp", "External", and
|
||||
# "Unknown" but any value may be used.
|
||||
attributetype ( 2.16.840.1.113730.3.1.4
|
||||
NAME 'employeeType'
|
||||
DESC 'RFC2798: type of employment for a person'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 )
|
||||
|
||||
# jpegPhoto
|
||||
# Used to store one or more images of a person using the JPEG File
|
||||
# Interchange Format [JFIF].
|
||||
# Note that the jpegPhoto attribute type was defined for use in the
|
||||
# Internet X.500 pilots but no referencable definition for it could be
|
||||
# located.
|
||||
attributetype ( 0.9.2342.19200300.100.1.60
|
||||
NAME 'jpegPhoto'
|
||||
DESC 'RFC2798: a JPEG image'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.28 )
|
||||
|
||||
# preferredLanguage
|
||||
# Used to indicate an individual's preferred written or spoken
|
||||
# language. This is useful for international correspondence or human-
|
||||
# computer interaction. Values for this attribute type MUST conform to
|
||||
# the definition of the Accept-Language header field defined in
|
||||
# [RFC2068] with one exception: the sequence "Accept-Language" ":"
|
||||
# should be omitted. This is a single valued attribute type.
|
||||
attributetype ( 2.16.840.1.113730.3.1.39
|
||||
NAME 'preferredLanguage'
|
||||
DESC 'RFC2798: preferred written or spoken language for a person'
|
||||
EQUALITY caseIgnoreMatch
|
||||
SUBSTR caseIgnoreSubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.15
|
||||
SINGLE-VALUE )
|
||||
|
||||
# userSMIMECertificate
|
||||
# A PKCS#7 [RFC2315] SignedData, where the content that is signed is
|
||||
# ignored by consumers of userSMIMECertificate values. It is
|
||||
# recommended that values have a `contentType' of data with an absent
|
||||
# `content' field. Values of this attribute contain a person's entire
|
||||
# certificate chain and an smimeCapabilities field [RFC2633] that at a
|
||||
# minimum describes their SMIME algorithm capabilities. Values for
|
||||
# this attribute are to be stored and requested in binary form, as
|
||||
# 'userSMIMECertificate;binary'. If available, this attribute is
|
||||
# preferred over the userCertificate attribute for S/MIME applications.
|
||||
## OpenLDAP note: ";binary" transfer should NOT be used as syntax is binary
|
||||
attributetype ( 2.16.840.1.113730.3.1.40
|
||||
NAME 'userSMIMECertificate'
|
||||
DESC 'RFC2798: PKCS#7 SignedData used to support S/MIME'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
|
||||
|
||||
# userPKCS12
|
||||
# PKCS #12 [PKCS12] provides a format for exchange of personal identity
|
||||
# information. When such information is stored in a directory service,
|
||||
# the userPKCS12 attribute should be used. This attribute is to be stored
|
||||
# and requested in binary form, as 'userPKCS12;binary'. The attribute
|
||||
# values are PFX PDUs stored as binary data.
|
||||
## OpenLDAP note: ";binary" transfer should NOT be used as syntax is binary
|
||||
attributetype ( 2.16.840.1.113730.3.1.216
|
||||
NAME 'userPKCS12'
|
||||
DESC 'RFC2798: personal identity information, a PKCS #12 PFX'
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.5 )
|
||||
|
||||
|
||||
# inetOrgPerson
|
||||
# The inetOrgPerson represents people who are associated with an
|
||||
# organization in some way. It is a structural class and is derived
|
||||
# from the organizationalPerson which is defined in X.521 [X521].
|
||||
objectclass ( 2.16.840.1.113730.3.2.2
|
||||
NAME 'inetOrgPerson'
|
||||
DESC 'RFC2798: Internet Organizational Person'
|
||||
SUP organizationalPerson
|
||||
STRUCTURAL
|
||||
MAY (
|
||||
audio $ businessCategory $ carLicense $ departmentNumber $
|
||||
displayName $ employeeNumber $ employeeType $ givenName $
|
||||
homePhone $ homePostalAddress $ initials $ jpegPhoto $
|
||||
labeledURI $ mail $ manager $ mobile $ o $ pager $
|
||||
photo $ roomNumber $ secretary $ uid $ userCertificate $
|
||||
x500uniqueIdentifier $ preferredLanguage $
|
||||
userSMIMECertificate $ userPKCS12 )
|
||||
)
|
75
connector/ldap/testdata/misc.schema
vendored
75
connector/ldap/testdata/misc.schema
vendored
|
@ -1,75 +0,0 @@
|
|||
# misc.schema -- assorted schema definitions
|
||||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 1998-2016 The OpenLDAP Foundation.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
#
|
||||
# Assorted definitions from several sources, including
|
||||
# ''works in progress''. Contents of this file are
|
||||
# subject to change (including deletion) without notice.
|
||||
#
|
||||
# Not recommended for production use!
|
||||
# Use with extreme caution!
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# draft-lachman-laser-ldap-mail-routing-02.txt !!!EXPIRED!!!
|
||||
# (a work in progress)
|
||||
#
|
||||
attributetype ( 2.16.840.1.113730.3.1.13
|
||||
NAME 'mailLocalAddress'
|
||||
DESC 'RFC822 email address of this recipient'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
|
||||
|
||||
attributetype ( 2.16.840.1.113730.3.1.18
|
||||
NAME 'mailHost'
|
||||
DESC 'FQDN of the SMTP/MTA of this recipient'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
|
||||
SINGLE-VALUE )
|
||||
|
||||
attributetype ( 2.16.840.1.113730.3.1.47
|
||||
NAME 'mailRoutingAddress'
|
||||
DESC 'RFC822 routing address of this recipient'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256}
|
||||
SINGLE-VALUE )
|
||||
|
||||
# I-D leaves this OID TBD.
|
||||
# iPlanet uses 2.16.840.1.113.730.3.2.147 but that is an
|
||||
# improperly delegated OID. A typo is likely.
|
||||
objectclass ( 2.16.840.1.113730.3.2.147
|
||||
NAME 'inetLocalMailRecipient'
|
||||
DESC 'Internet local mail recipient'
|
||||
SUP top AUXILIARY
|
||||
MAY ( mailLocalAddress $ mailHost $ mailRoutingAddress ) )
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# draft-srivastava-ldap-mail-00.txt !!!EXPIRED!!!
|
||||
# (a work in progress)
|
||||
#
|
||||
attributetype ( 1.3.6.1.4.1.42.2.27.2.1.15
|
||||
NAME 'rfc822MailMember'
|
||||
DESC 'rfc822 mail address of group member(s)'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
|
||||
#-----------------------------------------------------------
|
||||
# !!!no I-D!!!
|
||||
# (a work in progress)
|
||||
#
|
||||
objectclass ( 1.3.6.1.4.1.42.2.27.1.2.5
|
||||
NAME 'nisMailAlias'
|
||||
DESC 'NIS mail alias'
|
||||
SUP top STRUCTURAL
|
||||
MUST cn
|
||||
MAY rfc822MailMember )
|
237
connector/ldap/testdata/nis.schema
vendored
237
connector/ldap/testdata/nis.schema
vendored
|
@ -1,237 +0,0 @@
|
|||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 1998-2016 The OpenLDAP Foundation.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
# Definitions from RFC2307 (Experimental)
|
||||
# An Approach for Using LDAP as a Network Information Service
|
||||
|
||||
# Depends upon core.schema and cosine.schema
|
||||
|
||||
# Note: The definitions in RFC2307 are given in syntaxes closely related
|
||||
# to those in RFC2252, however, some liberties are taken that are not
|
||||
# supported by RFC2252. This file has been written following RFC2252
|
||||
# strictly.
|
||||
|
||||
# OID Base is iso(1) org(3) dod(6) internet(1) directory(1) nisSchema(1).
|
||||
# i.e. nisSchema in RFC2307 is 1.3.6.1.1.1
|
||||
#
|
||||
# Syntaxes are under 1.3.6.1.1.1.0 (two new syntaxes are defined)
|
||||
# validaters for these syntaxes are incomplete, they only
|
||||
# implement printable string validation (which is good as the
|
||||
# common use of these syntaxes violates the specification).
|
||||
# Attribute types are under 1.3.6.1.1.1.1
|
||||
# Object classes are under 1.3.6.1.1.1.2
|
||||
|
||||
# Attribute Type Definitions
|
||||
|
||||
# builtin
|
||||
#attributetype ( 1.3.6.1.1.1.1.0 NAME 'uidNumber'
|
||||
# DESC 'An integer uniquely identifying a user in an administrative domain'
|
||||
# EQUALITY integerMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
# builtin
|
||||
#attributetype ( 1.3.6.1.1.1.1.1 NAME 'gidNumber'
|
||||
# DESC 'An integer uniquely identifying a group in an administrative domain'
|
||||
# EQUALITY integerMatch
|
||||
# SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.2 NAME 'gecos'
|
||||
DESC 'The GECOS field; the common name'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SUBSTR caseIgnoreIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.3 NAME 'homeDirectory'
|
||||
DESC 'The absolute path to the home directory'
|
||||
EQUALITY caseExactIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.4 NAME 'loginShell'
|
||||
DESC 'The path to the login shell'
|
||||
EQUALITY caseExactIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.5 NAME 'shadowLastChange'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.6 NAME 'shadowMin'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.7 NAME 'shadowMax'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.8 NAME 'shadowWarning'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.9 NAME 'shadowInactive'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.10 NAME 'shadowExpire'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.11 NAME 'shadowFlag'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.12 NAME 'memberUid'
|
||||
EQUALITY caseExactIA5Match
|
||||
SUBSTR caseExactIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.13 NAME 'memberNisNetgroup'
|
||||
EQUALITY caseExactIA5Match
|
||||
SUBSTR caseExactIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.14 NAME 'nisNetgroupTriple'
|
||||
DESC 'Netgroup triple'
|
||||
SYNTAX 1.3.6.1.1.1.0.0 )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.15 NAME 'ipServicePort'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.16 NAME 'ipServiceProtocol'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.17 NAME 'ipProtocolNumber'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.18 NAME 'oncRpcNumber'
|
||||
EQUALITY integerMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.19 NAME 'ipHostNumber'
|
||||
DESC 'IP address'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.20 NAME 'ipNetworkNumber'
|
||||
DESC 'IP network'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.21 NAME 'ipNetmaskNumber'
|
||||
DESC 'IP netmask'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} SINGLE-VALUE )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.22 NAME 'macAddress'
|
||||
DESC 'MAC address'
|
||||
EQUALITY caseIgnoreIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{128} )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.23 NAME 'bootParameter'
|
||||
DESC 'rpc.bootparamd parameter'
|
||||
SYNTAX 1.3.6.1.1.1.0.1 )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.24 NAME 'bootFile'
|
||||
DESC 'Boot image name'
|
||||
EQUALITY caseExactIA5Match
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.26 NAME 'nisMapName'
|
||||
SUP name )
|
||||
|
||||
attributetype ( 1.3.6.1.1.1.1.27 NAME 'nisMapEntry'
|
||||
EQUALITY caseExactIA5Match
|
||||
SUBSTR caseExactIA5SubstringsMatch
|
||||
SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{1024} SINGLE-VALUE )
|
||||
|
||||
# Object Class Definitions
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount'
|
||||
DESC 'Abstraction of an account with POSIX attributes'
|
||||
SUP top AUXILIARY
|
||||
MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory )
|
||||
MAY ( userPassword $ loginShell $ gecos $ description ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.1 NAME 'shadowAccount'
|
||||
DESC 'Additional attributes for shadow passwords'
|
||||
SUP top AUXILIARY
|
||||
MUST uid
|
||||
MAY ( userPassword $ shadowLastChange $ shadowMin $
|
||||
shadowMax $ shadowWarning $ shadowInactive $
|
||||
shadowExpire $ shadowFlag $ description ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup'
|
||||
DESC 'Abstraction of a group of accounts'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ gidNumber )
|
||||
MAY ( userPassword $ memberUid $ description ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.3 NAME 'ipService'
|
||||
DESC 'Abstraction an Internet Protocol service'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ ipServicePort $ ipServiceProtocol )
|
||||
MAY ( description ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.4 NAME 'ipProtocol'
|
||||
DESC 'Abstraction of an IP protocol'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ ipProtocolNumber $ description )
|
||||
MAY description )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.5 NAME 'oncRpc'
|
||||
DESC 'Abstraction of an ONC/RPC binding'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ oncRpcNumber $ description )
|
||||
MAY description )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.6 NAME 'ipHost'
|
||||
DESC 'Abstraction of a host, an IP device'
|
||||
SUP top AUXILIARY
|
||||
MUST ( cn $ ipHostNumber )
|
||||
MAY ( l $ description $ manager ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.7 NAME 'ipNetwork'
|
||||
DESC 'Abstraction of an IP network'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ ipNetworkNumber )
|
||||
MAY ( ipNetmaskNumber $ l $ description $ manager ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.8 NAME 'nisNetgroup'
|
||||
DESC 'Abstraction of a netgroup'
|
||||
SUP top STRUCTURAL
|
||||
MUST cn
|
||||
MAY ( nisNetgroupTriple $ memberNisNetgroup $ description ) )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.9 NAME 'nisMap'
|
||||
DESC 'A generic abstraction of a NIS map'
|
||||
SUP top STRUCTURAL
|
||||
MUST nisMapName
|
||||
MAY description )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.10 NAME 'nisObject'
|
||||
DESC 'An entry in a NIS map'
|
||||
SUP top STRUCTURAL
|
||||
MUST ( cn $ nisMapEntry $ nisMapName )
|
||||
MAY description )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.11 NAME 'ieee802Device'
|
||||
DESC 'A device with a MAC address'
|
||||
SUP top AUXILIARY
|
||||
MAY macAddress )
|
||||
|
||||
objectclass ( 1.3.6.1.1.1.2.12 NAME 'bootableDevice'
|
||||
DESC 'A device with boot parameters'
|
||||
SUP top AUXILIARY
|
||||
MAY ( bootFile $ bootParameter ) )
|
54
connector/ldap/testdata/openldap.schema
vendored
54
connector/ldap/testdata/openldap.schema
vendored
|
@ -1,54 +0,0 @@
|
|||
# $OpenLDAP$
|
||||
## This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||
##
|
||||
## Copyright 1998-2016 The OpenLDAP Foundation.
|
||||
## All rights reserved.
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted only as authorized by the OpenLDAP
|
||||
## Public License.
|
||||
##
|
||||
## A copy of this license is available in the file LICENSE in the
|
||||
## top-level directory of the distribution or, alternatively, at
|
||||
## <http://www.OpenLDAP.org/license.html>.
|
||||
|
||||
#
|
||||
# OpenLDAP Project's directory schema items
|
||||
#
|
||||
# depends upon:
|
||||
# core.schema
|
||||
# cosine.schema
|
||||
# inetorgperson.schema
|
||||
#
|
||||
# These are provided for informational purposes only.
|
||||
|
||||
objectIdentifier OpenLDAProot 1.3.6.1.4.1.4203
|
||||
|
||||
objectIdentifier OpenLDAP OpenLDAProot:1
|
||||
objectIdentifier OpenLDAPattributeType OpenLDAP:3
|
||||
objectIdentifier OpenLDAPobjectClass OpenLDAP:4
|
||||
|
||||
objectClass ( OpenLDAPobjectClass:3
|
||||
NAME 'OpenLDAPorg'
|
||||
DESC 'OpenLDAP Organizational Object'
|
||||
SUP organization
|
||||
MAY ( buildingName $ displayName $ labeledURI ) )
|
||||
|
||||
objectClass ( OpenLDAPobjectClass:4
|
||||
NAME 'OpenLDAPou'
|
||||
DESC 'OpenLDAP Organizational Unit Object'
|
||||
SUP organizationalUnit
|
||||
MAY ( buildingName $ displayName $ labeledURI $ o ) )
|
||||
|
||||
objectClass ( OpenLDAPobjectClass:5
|
||||
NAME 'OpenLDAPperson'
|
||||
DESC 'OpenLDAP Person'
|
||||
SUP ( pilotPerson $ inetOrgPerson )
|
||||
MUST ( uid $ cn )
|
||||
MAY ( givenName $ labeledURI $ o ) )
|
||||
|
||||
objectClass ( OpenLDAPobjectClass:6
|
||||
NAME 'OpenLDAPdisplayableObject'
|
||||
DESC 'OpenLDAP Displayable Object'
|
||||
AUXILIARY
|
||||
MAY displayName )
|
|
@ -3,12 +3,13 @@ package oidc
|
|||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/coreos/go-oidc"
|
||||
"golang.org/x/oauth2"
|
||||
|
@ -40,6 +41,9 @@ type Config struct {
|
|||
// Override the value of email_verifed to true in the returned claims
|
||||
InsecureSkipEmailVerified bool `json:"insecureSkipEmailVerified"`
|
||||
|
||||
// InsecureEnableGroups enables groups claims. This is disabled by default until https://github.com/dexidp/dex/issues/1065 is resolved
|
||||
InsecureEnableGroups bool `json:"insecureEnableGroups"`
|
||||
|
||||
// GetUserInfo uses the userinfo endpoint to get additional claims for
|
||||
// the token. This is especially useful where upstreams return "thin"
|
||||
// id tokens
|
||||
|
@ -60,6 +64,11 @@ var brokenAuthHeaderDomains = []string{
|
|||
"oktapreview.com",
|
||||
}
|
||||
|
||||
// connectorData stores information for sessions authenticated by this connector
|
||||
type connectorData struct {
|
||||
RefreshToken []byte
|
||||
}
|
||||
|
||||
// Detect auth header provider issues for known providers. This lets users
|
||||
// avoid having to explicitly set "basicAuthUnsupported" in their config.
|
||||
//
|
||||
|
@ -75,18 +84,6 @@ func knownBrokenAuthHeaderProvider(issuerURL string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// golang.org/x/oauth2 doesn't do internal locking. Need to do it in this
|
||||
// package ourselves and hope that other packages aren't calling it at the
|
||||
// same time.
|
||||
var registerMu = new(sync.Mutex)
|
||||
|
||||
func registerBrokenAuthHeaderProvider(url string) {
|
||||
registerMu.Lock()
|
||||
defer registerMu.Unlock()
|
||||
|
||||
oauth2.RegisterBrokenAuthHeaderProvider(url)
|
||||
}
|
||||
|
||||
// Open returns a connector which can be used to login users through an upstream
|
||||
// OpenID Connect provider.
|
||||
func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, err error) {
|
||||
|
@ -98,13 +95,15 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
|
|||
return nil, fmt.Errorf("failed to get provider: %v", err)
|
||||
}
|
||||
|
||||
endpoint := provider.Endpoint()
|
||||
|
||||
if c.BasicAuthUnsupported != nil {
|
||||
// Setting "basicAuthUnsupported" always overrides our detection.
|
||||
if *c.BasicAuthUnsupported {
|
||||
registerBrokenAuthHeaderProvider(provider.Endpoint().TokenURL)
|
||||
endpoint.AuthStyle = oauth2.AuthStyleInParams
|
||||
}
|
||||
} else if knownBrokenAuthHeaderProvider(c.Issuer) {
|
||||
registerBrokenAuthHeaderProvider(provider.Endpoint().TokenURL)
|
||||
endpoint.AuthStyle = oauth2.AuthStyleInParams
|
||||
}
|
||||
|
||||
scopes := []string{oidc.ScopeOpenID}
|
||||
|
@ -121,7 +120,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
|
|||
oauth2Config: &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: c.ClientSecret,
|
||||
Endpoint: provider.Endpoint(),
|
||||
Endpoint: endpoint,
|
||||
Scopes: scopes,
|
||||
RedirectURL: c.RedirectURI,
|
||||
},
|
||||
|
@ -132,6 +131,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
|
|||
cancel: cancel,
|
||||
hostedDomains: c.HostedDomains,
|
||||
insecureSkipEmailVerified: c.InsecureSkipEmailVerified,
|
||||
insecureEnableGroups: c.InsecureEnableGroups,
|
||||
getUserInfo: c.GetUserInfo,
|
||||
userIDKey: c.UserIDKey,
|
||||
userNameKey: c.UserNameKey,
|
||||
|
@ -152,6 +152,7 @@ type oidcConnector struct {
|
|||
logger log.Logger
|
||||
hostedDomains []string
|
||||
insecureSkipEmailVerified bool
|
||||
insecureEnableGroups bool
|
||||
getUserInfo bool
|
||||
userIDKey string
|
||||
userNameKey string
|
||||
|
@ -167,14 +168,19 @@ func (c *oidcConnector) LoginURL(s connector.Scopes, callbackURL, state string)
|
|||
return "", fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, c.redirectURI)
|
||||
}
|
||||
|
||||
var opts []oauth2.AuthCodeOption
|
||||
if len(c.hostedDomains) > 0 {
|
||||
preferredDomain := c.hostedDomains[0]
|
||||
if len(c.hostedDomains) > 1 {
|
||||
preferredDomain = "*"
|
||||
}
|
||||
return c.oauth2Config.AuthCodeURL(state, oauth2.SetAuthURLParam("hd", preferredDomain)), nil
|
||||
opts = append(opts, oauth2.SetAuthURLParam("hd", preferredDomain))
|
||||
}
|
||||
return c.oauth2Config.AuthCodeURL(state), nil
|
||||
|
||||
if s.OfflineAccess {
|
||||
opts = append(opts, oauth2.AccessTypeOffline, oauth2.SetAuthURLParam("prompt", "consent"))
|
||||
}
|
||||
return c.oauth2Config.AuthCodeURL(state, opts...), nil
|
||||
}
|
||||
|
||||
type oauth2Error struct {
|
||||
|
@ -199,11 +205,35 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
|
|||
return identity, fmt.Errorf("oidc: failed to get token: %v", err)
|
||||
}
|
||||
|
||||
return c.createIdentity(r.Context(), identity, token)
|
||||
}
|
||||
|
||||
// Refresh is used to refresh a session with the refresh token provided by the IdP
|
||||
func (c *oidcConnector) Refresh(ctx context.Context, s connector.Scopes, identity connector.Identity) (connector.Identity, error) {
|
||||
cd := connectorData{}
|
||||
err := json.Unmarshal(identity.ConnectorData, &cd)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to unmarshal connector data: %v", err)
|
||||
}
|
||||
|
||||
t := &oauth2.Token{
|
||||
RefreshToken: string(cd.RefreshToken),
|
||||
Expiry: time.Now().Add(-time.Hour),
|
||||
}
|
||||
token, err := c.oauth2Config.TokenSource(ctx, t).Token()
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to get refresh token: %v", err)
|
||||
}
|
||||
|
||||
return c.createIdentity(ctx, identity, token)
|
||||
}
|
||||
|
||||
func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.Identity, token *oauth2.Token) (connector.Identity, error) {
|
||||
rawIDToken, ok := token.Extra("id_token").(string)
|
||||
if !ok {
|
||||
return identity, errors.New("oidc: no id_token in token response")
|
||||
}
|
||||
idToken, err := c.verifier.Verify(r.Context(), rawIDToken)
|
||||
idToken, err := c.verifier.Verify(ctx, rawIDToken)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err)
|
||||
}
|
||||
|
@ -213,6 +243,17 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
|
|||
return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
|
||||
}
|
||||
|
||||
// We immediately want to run getUserInfo if configured before we validate the claims
|
||||
if c.getUserInfo {
|
||||
userInfo, err := c.provider.UserInfo(ctx, oauth2.StaticTokenSource(token))
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: error loading userinfo: %v", err)
|
||||
}
|
||||
if err := userInfo.Claims(&claims); err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to decode userinfo claims: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
userNameKey := "name"
|
||||
if c.userNameKey != "" {
|
||||
userNameKey = c.userNameKey
|
||||
|
@ -249,14 +290,13 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
|
|||
}
|
||||
}
|
||||
|
||||
if c.getUserInfo {
|
||||
userInfo, err := c.provider.UserInfo(r.Context(), oauth2.StaticTokenSource(token))
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: error loading userinfo: %v", err)
|
||||
}
|
||||
if err := userInfo.Claims(&claims); err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to decode userinfo claims: %v", err)
|
||||
}
|
||||
cd := connectorData{
|
||||
RefreshToken: []byte(token.RefreshToken),
|
||||
}
|
||||
|
||||
connData, err := json.Marshal(&cd)
|
||||
if err != nil {
|
||||
return identity, fmt.Errorf("oidc: failed to encode connector data: %v", err)
|
||||
}
|
||||
|
||||
identity = connector.Identity{
|
||||
|
@ -264,6 +304,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
|
|||
Username: name,
|
||||
Email: email,
|
||||
EmailVerified: emailVerified,
|
||||
ConnectorData: connData,
|
||||
}
|
||||
|
||||
if c.userIDKey != "" {
|
||||
|
@ -274,10 +315,18 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
|
|||
identity.UserID = userID
|
||||
}
|
||||
|
||||
return identity, nil
|
||||
}
|
||||
if c.insecureEnableGroups {
|
||||
vs, ok := claims["groups"].([]interface{})
|
||||
if ok {
|
||||
for _, v := range vs {
|
||||
if s, ok := v.(string); ok {
|
||||
identity.Groups = append(identity.Groups, s)
|
||||
} else {
|
||||
return identity, errors.New("malformed \"groups\" claim")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Refresh is implemented for backwards compatibility, even though it's a no-op.
|
||||
func (c *oidcConnector) Refresh(ctx context.Context, s connector.Scopes, identity connector.Identity) (connector.Identity, error) {
|
||||
return identity, nil
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
)
|
||||
|
||||
func TestKnownBrokenAuthHeaderProvider(t *testing.T) {
|
||||
|
@ -111,6 +112,7 @@ func TestHandleCallback(t *testing.T) {
|
|||
}
|
||||
defer testServer.Close()
|
||||
serverURL := testServer.URL
|
||||
basicAuth := true
|
||||
config := Config{
|
||||
Issuer: serverURL,
|
||||
ClientID: "clientID",
|
||||
|
@ -120,6 +122,7 @@ func TestHandleCallback(t *testing.T) {
|
|||
UserIDKey: tc.userIDKey,
|
||||
UserNameKey: tc.userNameKey,
|
||||
InsecureSkipEmailVerified: tc.insecureSkipEmailVerified,
|
||||
BasicAuthUnsupported: &basicAuth,
|
||||
}
|
||||
|
||||
conn, err := newConnector(config)
|
||||
|
|
|
@ -14,10 +14,12 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/beevik/etree"
|
||||
"github.com/dexidp/dex/connector"
|
||||
"github.com/dexidp/dex/pkg/log"
|
||||
dsig "github.com/russellhaering/goxmldsig"
|
||||
"github.com/russellhaering/goxmldsig/etreeutils"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
"github.com/dexidp/dex/pkg/groups"
|
||||
"github.com/dexidp/dex/pkg/log"
|
||||
)
|
||||
|
||||
// nolint
|
||||
|
@ -97,9 +99,9 @@ type Config struct {
|
|||
// If GroupsDelim is supplied the connector assumes groups are returned as a
|
||||
// single string instead of multiple attribute values. This delimiter will be
|
||||
// used split the groups string.
|
||||
GroupsDelim string `json:"groupsDelim"`
|
||||
|
||||
RedirectURI string `json:"redirectURI"`
|
||||
GroupsDelim string `json:"groupsDelim"`
|
||||
AllowedGroups []string `json:"allowedGroups"`
|
||||
RedirectURI string `json:"redirectURI"`
|
||||
|
||||
// Requested format of the NameID. The NameID value is is mapped to the ID Token
|
||||
// 'sub' claim.
|
||||
|
@ -154,16 +156,17 @@ func (c *Config) openConnector(logger log.Logger) (*provider, error) {
|
|||
}
|
||||
|
||||
p := &provider{
|
||||
entityIssuer: c.EntityIssuer,
|
||||
ssoIssuer: c.SSOIssuer,
|
||||
ssoURL: c.SSOURL,
|
||||
now: time.Now,
|
||||
usernameAttr: c.UsernameAttr,
|
||||
emailAttr: c.EmailAttr,
|
||||
groupsAttr: c.GroupsAttr,
|
||||
groupsDelim: c.GroupsDelim,
|
||||
redirectURI: c.RedirectURI,
|
||||
logger: logger,
|
||||
entityIssuer: c.EntityIssuer,
|
||||
ssoIssuer: c.SSOIssuer,
|
||||
ssoURL: c.SSOURL,
|
||||
now: time.Now,
|
||||
usernameAttr: c.UsernameAttr,
|
||||
emailAttr: c.EmailAttr,
|
||||
groupsAttr: c.GroupsAttr,
|
||||
groupsDelim: c.GroupsDelim,
|
||||
allowedGroups: c.AllowedGroups,
|
||||
redirectURI: c.RedirectURI,
|
||||
logger: logger,
|
||||
|
||||
nameIDPolicyFormat: c.NameIDPolicyFormat,
|
||||
}
|
||||
|
@ -232,10 +235,11 @@ type provider struct {
|
|||
validator *dsig.ValidationContext
|
||||
|
||||
// Attribute mappings
|
||||
usernameAttr string
|
||||
emailAttr string
|
||||
groupsAttr string
|
||||
groupsDelim string
|
||||
usernameAttr string
|
||||
emailAttr string
|
||||
groupsAttr string
|
||||
groupsDelim string
|
||||
allowedGroups []string
|
||||
|
||||
redirectURI string
|
||||
|
||||
|
@ -245,7 +249,6 @@ type provider struct {
|
|||
}
|
||||
|
||||
func (p *provider) POSTData(s connector.Scopes, id string) (action, value string, err error) {
|
||||
|
||||
r := &authnRequest{
|
||||
ProtocolBinding: bindingPOST,
|
||||
ID: id,
|
||||
|
@ -322,7 +325,7 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo str
|
|||
|
||||
// Status is a required element.
|
||||
if resp.Status == nil {
|
||||
return ident, fmt.Errorf("Response did not contain a Status element")
|
||||
return ident, fmt.Errorf("response did not contain a Status element")
|
||||
}
|
||||
|
||||
if err = p.validateStatus(resp.Status); err != nil {
|
||||
|
@ -388,11 +391,16 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo str
|
|||
return ident, fmt.Errorf("no attribute with name %q: %s", p.usernameAttr, attributes.names())
|
||||
}
|
||||
|
||||
if !s.Groups || p.groupsAttr == "" {
|
||||
if len(p.allowedGroups) == 0 && (!s.Groups || p.groupsAttr == "") {
|
||||
// Groups not requested or not configured. We're done.
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
if len(p.allowedGroups) > 0 && (!s.Groups || p.groupsAttr == "") {
|
||||
// allowedGroups set but no groups or groupsAttr. Disallowing.
|
||||
return ident, fmt.Errorf("user not a member of allowed groups")
|
||||
}
|
||||
|
||||
// Grab the groups.
|
||||
if p.groupsDelim != "" {
|
||||
groupsStr, ok := attributes.get(p.groupsAttr)
|
||||
|
@ -408,6 +416,21 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo str
|
|||
}
|
||||
ident.Groups = groups
|
||||
}
|
||||
|
||||
if len(p.allowedGroups) == 0 {
|
||||
// No allowed groups set, just return the ident
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
// Look for membership in one of the allowed groups
|
||||
groupMatches := groups.Filter(ident.Groups, p.allowedGroups)
|
||||
|
||||
if len(groupMatches) == 0 {
|
||||
// No group membership matches found, disallowing
|
||||
return ident, fmt.Errorf("user not a member of allowed groups")
|
||||
}
|
||||
|
||||
// Otherwise, we're good
|
||||
return ident, nil
|
||||
}
|
||||
|
||||
|
@ -445,7 +468,7 @@ func (p *provider) validateStatus(status *status) error {
|
|||
func (p *provider) validateSubject(subject *subject, inResponseTo string) error {
|
||||
// Optional according to the spec, but again, we're going to be strict here.
|
||||
if len(subject.SubjectConfirmations) == 0 {
|
||||
return fmt.Errorf("Subject contained no SubjectConfirmations")
|
||||
return fmt.Errorf("subject contained no SubjectConfirmations")
|
||||
}
|
||||
|
||||
var errs []error
|
||||
|
|
|
@ -49,9 +49,10 @@ type responseTest struct {
|
|||
entityIssuer string
|
||||
|
||||
// Attribute customization.
|
||||
usernameAttr string
|
||||
emailAttr string
|
||||
groupsAttr string
|
||||
usernameAttr string
|
||||
emailAttr string
|
||||
groupsAttr string
|
||||
allowedGroups []string
|
||||
|
||||
// Expected outcome of the test.
|
||||
wantErr bool
|
||||
|
@ -98,6 +99,96 @@ func TestGroups(t *testing.T) {
|
|||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGroupsWhitelist(t *testing.T) {
|
||||
test := responseTest{
|
||||
caFile: "testdata/ca.crt",
|
||||
respFile: "testdata/good-resp.xml",
|
||||
now: "2017-04-04T04:34:59.330Z",
|
||||
usernameAttr: "Name",
|
||||
emailAttr: "email",
|
||||
groupsAttr: "groups",
|
||||
allowedGroups: []string{"Admins"},
|
||||
inResponseTo: "6zmm5mguyebwvajyf2sdwwcw6m",
|
||||
redirectURI: "http://127.0.0.1:5556/dex/callback",
|
||||
wantIdent: connector.Identity{
|
||||
UserID: "eric.chiang+okta@coreos.com",
|
||||
Username: "Eric",
|
||||
Email: "eric.chiang+okta@coreos.com",
|
||||
EmailVerified: true,
|
||||
Groups: []string{"Admins", "Everyone"},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGroupsWhitelistEmpty(t *testing.T) {
|
||||
test := responseTest{
|
||||
caFile: "testdata/ca.crt",
|
||||
respFile: "testdata/good-resp.xml",
|
||||
now: "2017-04-04T04:34:59.330Z",
|
||||
usernameAttr: "Name",
|
||||
emailAttr: "email",
|
||||
groupsAttr: "groups",
|
||||
allowedGroups: []string{},
|
||||
inResponseTo: "6zmm5mguyebwvajyf2sdwwcw6m",
|
||||
redirectURI: "http://127.0.0.1:5556/dex/callback",
|
||||
wantIdent: connector.Identity{
|
||||
UserID: "eric.chiang+okta@coreos.com",
|
||||
Username: "Eric",
|
||||
Email: "eric.chiang+okta@coreos.com",
|
||||
EmailVerified: true,
|
||||
Groups: []string{"Admins", "Everyone"},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGroupsWhitelistDisallowed(t *testing.T) {
|
||||
test := responseTest{
|
||||
wantErr: true,
|
||||
caFile: "testdata/ca.crt",
|
||||
respFile: "testdata/good-resp.xml",
|
||||
now: "2017-04-04T04:34:59.330Z",
|
||||
usernameAttr: "Name",
|
||||
emailAttr: "email",
|
||||
groupsAttr: "groups",
|
||||
allowedGroups: []string{"Nope"},
|
||||
inResponseTo: "6zmm5mguyebwvajyf2sdwwcw6m",
|
||||
redirectURI: "http://127.0.0.1:5556/dex/callback",
|
||||
wantIdent: connector.Identity{
|
||||
UserID: "eric.chiang+okta@coreos.com",
|
||||
Username: "Eric",
|
||||
Email: "eric.chiang+okta@coreos.com",
|
||||
EmailVerified: true,
|
||||
Groups: []string{"Admins", "Everyone"},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
func TestGroupsWhitelistDisallowedNoGroupsOnIdent(t *testing.T) {
|
||||
test := responseTest{
|
||||
wantErr: true,
|
||||
caFile: "testdata/ca.crt",
|
||||
respFile: "testdata/good-resp.xml",
|
||||
now: "2017-04-04T04:34:59.330Z",
|
||||
usernameAttr: "Name",
|
||||
emailAttr: "email",
|
||||
groupsAttr: "groups",
|
||||
allowedGroups: []string{"Nope"},
|
||||
inResponseTo: "6zmm5mguyebwvajyf2sdwwcw6m",
|
||||
redirectURI: "http://127.0.0.1:5556/dex/callback",
|
||||
wantIdent: connector.Identity{
|
||||
UserID: "eric.chiang+okta@coreos.com",
|
||||
Username: "Eric",
|
||||
Email: "eric.chiang+okta@coreos.com",
|
||||
EmailVerified: true,
|
||||
Groups: []string{},
|
||||
},
|
||||
}
|
||||
test.run(t)
|
||||
}
|
||||
|
||||
// TestOkta tests against an actual response from Okta.
|
||||
func TestOkta(t *testing.T) {
|
||||
test := responseTest{
|
||||
|
@ -290,12 +381,13 @@ func loadCert(ca string) (*x509.Certificate, error) {
|
|||
|
||||
func (r responseTest) run(t *testing.T) {
|
||||
c := Config{
|
||||
CA: r.caFile,
|
||||
UsernameAttr: r.usernameAttr,
|
||||
EmailAttr: r.emailAttr,
|
||||
GroupsAttr: r.groupsAttr,
|
||||
RedirectURI: r.redirectURI,
|
||||
EntityIssuer: r.entityIssuer,
|
||||
CA: r.caFile,
|
||||
UsernameAttr: r.usernameAttr,
|
||||
EmailAttr: r.emailAttr,
|
||||
GroupsAttr: r.groupsAttr,
|
||||
RedirectURI: r.redirectURI,
|
||||
EntityIssuer: r.entityIssuer,
|
||||
AllowedGroups: r.allowedGroups,
|
||||
// Never logging in, don't need this.
|
||||
SSOURL: "http://foo.bar/",
|
||||
}
|
||||
|
|
9
go.mod
9
go.mod
|
@ -1,6 +1,7 @@
|
|||
module github.com/dexidp/dex
|
||||
|
||||
require (
|
||||
github.com/Microsoft/hcsshim v0.8.7 // indirect
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/coreos/bbolt v1.3.3 // indirect
|
||||
github.com/coreos/etcd v3.3.13+incompatible
|
||||
|
@ -12,7 +13,6 @@ require (
|
|||
github.com/felixge/httpsnoop v1.0.0
|
||||
github.com/ghodss/yaml v1.0.0
|
||||
github.com/go-sql-driver/mysql v1.4.1
|
||||
github.com/gogo/protobuf v1.2.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
|
||||
github.com/golang/protobuf v1.3.2
|
||||
github.com/google/btree v1.0.0 // indirect
|
||||
|
@ -29,16 +29,15 @@ require (
|
|||
github.com/kylelemons/godebug v1.1.0
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/mattn/go-sqlite3 v1.11.0
|
||||
github.com/pkg/errors v0.8.1 // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/prometheus/client_golang v1.0.0
|
||||
github.com/prometheus/common v0.6.0 // indirect
|
||||
github.com/prometheus/procfs v0.0.3 // indirect
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/soheilhy/cmux v0.1.4 // indirect
|
||||
github.com/spf13/cobra v0.0.5
|
||||
github.com/stretchr/testify v1.3.0
|
||||
github.com/testcontainers/testcontainers-go v0.0.9
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
|
||||
go.etcd.io/bbolt v1.3.3 // indirect
|
||||
|
@ -49,9 +48,9 @@ require (
|
|||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect
|
||||
golang.org/x/tools v0.0.0-20190813214729-9dba7caff850 // indirect
|
||||
google.golang.org/api v0.10.0
|
||||
google.golang.org/appengine v1.6.1 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 // indirect
|
||||
google.golang.org/grpc v1.23.0
|
||||
|
@ -59,3 +58,5 @@ require (
|
|||
gopkg.in/ldap.v2 v2.5.1
|
||||
gopkg.in/square/go-jose.v2 v2.3.1
|
||||
)
|
||||
|
||||
go 1.13
|
||||
|
|
135
go.sum
135
go.sum
|
@ -1,6 +1,17 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0 h1:ROfEUZz+Gh5pa62DJWXSaonyu3StP6EA6lPEXPI6mCo=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5 h1:ygIc8M6trr62pF5DucadTWGdEB4mEyvzi0e2nbcmcyA=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.7 h1:ptnOoufxGSzauVTsdE+wMYnCWA301PdoN4xg5oRdZpg=
|
||||
github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
|
@ -9,7 +20,19 @@ github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62
|
|||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
|
||||
github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
|
||||
github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc h1:TP+534wVlf61smEIq1nwLLAjQVEK2EADoW3CX9AuT+8=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
|
||||
github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
|
||||
github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
|
||||
github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
|
||||
github.com/coreos/bbolt v1.3.3 h1:n6AiVyVRKQFNb6mJlwESEvvLoDyiTzXX7ORAUlkeBdY=
|
||||
github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
|
@ -22,6 +45,7 @@ github.com/coreos/go-semver v0.2.0 h1:3Jm3tLmsgAYcjC+4Up7hJrFBPr+n7rAqYeSw/SZazu
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
|
||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
|
||||
|
@ -32,19 +56,34 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible h1:dvc1KSkIYTVjZgHf/CTC2diTYC8PzhaA5sFISRfNVrE=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661 h1:ZuxGvIvF01nfc/G9RJ5Q7Va1zQE2WJyG18Zv3DqCEf4=
|
||||
github.com/docker/docker v0.7.3-0.20190506211059-b20a14b54661/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
|
||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/felixge/httpsnoop v1.0.0 h1:gh8fMGz0rlOv/1WmRZm7OgncIOTsAj21iNJot48omJQ=
|
||||
github.com/felixge/httpsnoop v1.0.0/go.mod h1:3+D9sFq0ahK/JeJPhCBUV1xlf4/eIYrUQaxulT0VzX8=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-redis/redis v6.15.6+incompatible h1:H9evprGPLI8+ci7fxQx6WNZHJSb7be8FqJQRhdQZ5Sg=
|
||||
github.com/go-redis/redis v6.15.6+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
|
@ -52,17 +91,27 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
|
|||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/handlers v1.4.2 h1:0QniY0USkHQ1RGCLfKxeNHK9bkDHGRYGNDFBCS+YARg=
|
||||
github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
|
||||
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q=
|
||||
|
@ -75,7 +124,14 @@ github.com/grpc-ecosystem/grpc-gateway v1.9.5 h1:UImYN5qQ8tuGpGE16ZmjvcTtTw24zw1
|
|||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69 h1:7xsUJsB2NrdcttQPa7JLEaGzvdbk7KvfrjgHZXOQRo0=
|
||||
github.com/gtank/cryptopasta v0.0.0-20170601214702-1f550f6f2f69/go.mod h1:YLEMZOtU+AZ7dhN9T/IpGhXVGly2bvkJQ+zxj3WeVQo=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
|
||||
|
@ -83,6 +139,7 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
|
|||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7 h1:KfgG9LzI+pYjr4xvmz/5H4FXjokeP+rlHLhv3iH62Fo=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
|
@ -112,7 +169,24 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
|||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c h1:nXxl5PrvVm2L/wCy8dQu6DMTwH4oIuGN8GJDAlqDdVE=
|
||||
github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.8.0 h1:VkHVNpR4iVnU8XQR6DBm8BqYjN7CRzw+xKUbVVbbW9w=
|
||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
|
@ -132,13 +206,16 @@ github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkp
|
|||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/prometheus/procfs v0.0.5 h1:3+auTFlqw+ZaQYJARz6ArODtkaIwtvBTx3N2NehQlL8=
|
||||
github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7 h1:J4AOUcOh/t1XbQcJfkEqhzgvMJ2tDxdCVvmHxW5QXao=
|
||||
github.com/russellhaering/goxmldsig v0.0.0-20180430223755-7acd5e4a6ef7/go.mod h1:Oz4y6ImuOQZxynhbSXk7btjEfNBtGlj2dcaOvXl2FSM=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E=
|
||||
|
@ -157,14 +234,25 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/testcontainers/testcontainers-go v0.0.9 h1:mwvFz+FkuQMqQ9oLkG4cVzPsZTRmrCo2NcaerJNaptA=
|
||||
github.com/testcontainers/testcontainers-go v0.0.9/go.mod h1:0Qe9qqjNZgxHzzdHPWwmQ2D49FFO7920hLdJ4yUJXJI=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.etcd.io/bbolt v1.3.3 h1:MUGmc65QhB3pIlaQ5bB4LwqSj6GIonVJXpZiaKNyaKk=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
|
@ -181,64 +269,92 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
|
|||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422 h1:QzoH/1pFpZguR8NrRHLcO6jKqfv2zpuSqZLgdm7ZmjI=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180810170437-e96c4e24768d/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190813214729-9dba7caff850 h1:I+2i9HEjvv+3iJrNMhjMJOMF/FXz9eI08iBlDF4w24I=
|
||||
golang.org/x/tools v0.0.0-20190813214729-9dba7caff850/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.10.0 h1:7tmAxx3oKE98VMZ+SBZzvYYWRQ9HODBxmC8mXUsraSQ=
|
||||
google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64 h1:iKtrH9Y8mcbADOP0YFaEMth7OfuHY9xHOwNj4znpM1A=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
|
@ -247,14 +363,25 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy
|
|||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ldap.v2 v2.5.1 h1:wiu0okdNfjlBzg6UWvd1Hn8Y+Ux17/u/4nlk4CQr6tU=
|
||||
gopkg.in/ldap.v2 v2.5.1/go.mod h1:oI0cpe/D7HRtBQl8aTg+ZmzFUAvu4lsv3eLXMLGFxWk=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v0.0.0-20181223230014-1083505acf35/go.mod h1:R//lfYlUuTOTfblYI3lGoAAAebUdzjvbmQsuB7Ykd90=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
result=$( go fmt $@ )
|
||||
if [[ $result != "" ]]; then
|
||||
>&2 echo "The following files are not formatted correctly: $result"
|
||||
exit 1
|
||||
fi
|
|
@ -218,7 +218,6 @@ func (d dexAPI) DeletePassword(ctx context.Context, req *api.DeletePasswordReq)
|
|||
return nil, fmt.Errorf("delete password: %v", err)
|
||||
}
|
||||
return &api.DeletePasswordResp{}, nil
|
||||
|
||||
}
|
||||
|
||||
func (d dexAPI) GetVersion(ctx context.Context, req *api.VersionReq) (*api.VersionResp, error) {
|
||||
|
@ -248,7 +247,6 @@ func (d dexAPI) ListPasswords(ctx context.Context, req *api.ListPasswordReq) (*a
|
|||
return &api.ListPasswordResp{
|
||||
Passwords: passwords,
|
||||
}, nil
|
||||
|
||||
}
|
||||
|
||||
func (d dexAPI) VerifyPassword(ctx context.Context, req *api.VerifyPasswordReq) (*api.VerifyPasswordResp, error) {
|
||||
|
|
|
@ -167,7 +167,6 @@ func TestPassword(t *testing.T) {
|
|||
if _, err := client.DeletePassword(ctx, &deleteReq); err != nil {
|
||||
t.Fatalf("Unable to delete password: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Ensures checkCost returns expected values
|
||||
|
@ -495,7 +494,6 @@ func TestUpdateClient(t *testing.T) {
|
|||
if tc.cleanup != nil {
|
||||
tc.cleanup(t, tc.req.Id)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,8 +45,8 @@ func (s *Server) newHealthChecker(ctx context.Context) http.Handler {
|
|||
return h
|
||||
}
|
||||
|
||||
// healthChecker periodically performs health checks on server dependenices.
|
||||
// Currently, it only checks that the storage layer is avialable.
|
||||
// healthChecker periodically performs health checks on server dependencies.
|
||||
// Currently, it only checks that the storage layer is available.
|
||||
type healthChecker struct {
|
||||
s *Server
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (h *healthChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
h.mu.RUnlock()
|
||||
|
||||
if err != nil {
|
||||
h.s.renderError(w, http.StatusInternalServerError, "Health check failed.")
|
||||
h.s.renderError(r, w, http.StatusInternalServerError, "Health check failed.")
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "Health check passed in %s", t)
|
||||
|
@ -112,13 +112,13 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
|
|||
keys, err := s.storage.GetKeys()
|
||||
if err != nil {
|
||||
s.logger.Errorf("failed to get keys: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Internal server error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
|
||||
return
|
||||
}
|
||||
|
||||
if keys.SigningKeyPub == nil {
|
||||
s.logger.Errorf("No public keys found.")
|
||||
s.renderError(w, http.StatusInternalServerError, "Internal server error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -133,7 +133,7 @@ func (s *Server) handlePublicKeys(w http.ResponseWriter, r *http.Request) {
|
|||
data, err := json.MarshalIndent(jwks, "", " ")
|
||||
if err != nil {
|
||||
s.logger.Errorf("failed to marshal discovery data: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Internal server error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
|
||||
return
|
||||
}
|
||||
maxAge := keys.NextRotation.Sub(s.now())
|
||||
|
@ -214,7 +214,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
|
|||
status = err.Status()
|
||||
}
|
||||
|
||||
s.renderError(w, status, err.Error())
|
||||
s.renderError(r, w, status, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -226,14 +226,14 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
|
|||
authReq.Expiry = s.now().Add(s.authRequestsValidFor)
|
||||
if err := s.storage.CreateAuthRequest(*authReq); err != nil {
|
||||
s.logger.Errorf("Failed to create authorization request: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Failed to connect to the database.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Failed to connect to the database.")
|
||||
return
|
||||
}
|
||||
|
||||
connectors, err := s.storage.ListConnectors()
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to get list of connectors: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Failed to retrieve connector list.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve connector list.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -271,7 +271,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
|
|||
i++
|
||||
}
|
||||
|
||||
if err := s.templates.login(w, connectorInfos); err != nil {
|
||||
if err := s.templates.login(r, w, connectorInfos, r.URL.Path); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
conn, err := s.getConnector(connID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to create authorization request: %v", err)
|
||||
s.renderError(w, http.StatusBadRequest, "Requested resource does not exist")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -291,9 +291,9 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
if err != nil {
|
||||
s.logger.Errorf("Failed to get auth request: %v", err)
|
||||
if err == storage.ErrNotFound {
|
||||
s.renderError(w, http.StatusBadRequest, "Login session expired.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Login session expired.")
|
||||
} else {
|
||||
s.renderError(w, http.StatusInternalServerError, "Database error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Database error.")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -306,7 +306,7 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
if err := s.storage.UpdateAuthRequest(authReqID, updater); err != nil {
|
||||
s.logger.Errorf("Failed to set connector ID on auth request: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Database error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Database error.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -324,19 +324,19 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
callbackURL, err := conn.LoginURL(scopes, s.absURL("/callback"), authReqID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Connector %q returned error when creating callback: %v", connID, err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Login error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
|
||||
return
|
||||
}
|
||||
http.Redirect(w, r, callbackURL, http.StatusFound)
|
||||
case connector.PasswordConnector:
|
||||
if err := s.templates.password(w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink); err != nil {
|
||||
if err := s.templates.password(r, w, r.URL.String(), "", usernamePrompt(conn), false, showBacklink, r.URL.Path); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
case connector.SAMLConnector:
|
||||
action, value, err := conn.POSTData(scopes, authReqID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Creating SAML data: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Connector Login Error")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Connector Login Error")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -358,12 +358,12 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
</body>
|
||||
</html>`, action, value, authReqID)
|
||||
default:
|
||||
s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
}
|
||||
case http.MethodPost:
|
||||
passwordConnector, ok := conn.Connector.(connector.PasswordConnector)
|
||||
if !ok {
|
||||
s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -373,11 +373,11 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
identity, ok, err := passwordConnector.Login(r.Context(), scopes, username, password)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to login user: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
|
||||
s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Login error: %v", err))
|
||||
return
|
||||
}
|
||||
if !ok {
|
||||
if err := s.templates.password(w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink); err != nil {
|
||||
if err := s.templates.password(r, w, r.URL.String(), username, usernamePrompt(passwordConnector), true, showBacklink, r.URL.Path); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
return
|
||||
|
@ -385,13 +385,13 @@ func (s *Server) handleConnectorLogin(w http.ResponseWriter, r *http.Request) {
|
|||
redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to finalize login: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Login error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
|
||||
return
|
||||
}
|
||||
|
||||
http.Redirect(w, r, redirectURL, http.StatusSeeOther)
|
||||
default:
|
||||
s.renderError(w, http.StatusBadRequest, "Unsupported request method.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Unsupported request method.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,16 +400,16 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
|
|||
switch r.Method {
|
||||
case http.MethodGet: // OAuth2 callback
|
||||
if authID = r.URL.Query().Get("state"); authID == "" {
|
||||
s.renderError(w, http.StatusBadRequest, "User session error.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "User session error.")
|
||||
return
|
||||
}
|
||||
case http.MethodPost: // SAML POST binding
|
||||
if authID = r.PostFormValue("RelayState"); authID == "" {
|
||||
s.renderError(w, http.StatusBadRequest, "User session error.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "User session error.")
|
||||
return
|
||||
}
|
||||
default:
|
||||
s.renderError(w, http.StatusBadRequest, "Method not supported")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Method not supported")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -417,24 +417,24 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
|
|||
if err != nil {
|
||||
if err == storage.ErrNotFound {
|
||||
s.logger.Errorf("Invalid 'state' parameter provided: %v", err)
|
||||
s.renderError(w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Requested resource does not exist.")
|
||||
return
|
||||
}
|
||||
s.logger.Errorf("Failed to get auth request: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Database error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Database error.")
|
||||
return
|
||||
}
|
||||
|
||||
if connID := mux.Vars(r)["connector"]; connID != "" && connID != authReq.ConnectorID {
|
||||
s.logger.Errorf("Connector mismatch: authentication started with id %q, but callback for id %q was triggered", authReq.ConnectorID, connID)
|
||||
s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
conn, err := s.getConnector(authReq.ConnectorID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to get connector with id %q : %v", authReq.ConnectorID, err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -443,32 +443,32 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
|
|||
case connector.CallbackConnector:
|
||||
if r.Method != http.MethodGet {
|
||||
s.logger.Errorf("SAML request mapped to OAuth2 connector")
|
||||
s.renderError(w, http.StatusBadRequest, "Invalid request")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Invalid request")
|
||||
return
|
||||
}
|
||||
identity, err = conn.HandleCallback(parseScopes(authReq.Scopes), r)
|
||||
case connector.SAMLConnector:
|
||||
if r.Method != http.MethodPost {
|
||||
s.logger.Errorf("OAuth2 request mapped to SAML connector")
|
||||
s.renderError(w, http.StatusBadRequest, "Invalid request")
|
||||
s.renderError(r, w, http.StatusBadRequest, "Invalid request")
|
||||
return
|
||||
}
|
||||
identity, err = conn.HandlePOST(parseScopes(authReq.Scopes), r.PostFormValue("SAMLResponse"), authReq.ID)
|
||||
default:
|
||||
s.renderError(w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Requested resource does not exist.")
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to authenticate: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
|
||||
s.renderError(r, w, http.StatusInternalServerError, fmt.Sprintf("Failed to authenticate: %v", err))
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := s.finalizeLogin(identity, authReq, conn.Connector)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to finalize login: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Login error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Login error.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -479,11 +479,12 @@ func (s *Server) handleConnectorCallback(w http.ResponseWriter, r *http.Request)
|
|||
// the approval page's path.
|
||||
func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.AuthRequest, conn connector.Connector) (string, error) {
|
||||
claims := storage.Claims{
|
||||
UserID: identity.UserID,
|
||||
Username: identity.Username,
|
||||
Email: identity.Email,
|
||||
EmailVerified: identity.EmailVerified,
|
||||
Groups: identity.Groups,
|
||||
UserID: identity.UserID,
|
||||
Username: identity.Username,
|
||||
PreferredUsername: identity.PreferredUsername,
|
||||
Email: identity.Email,
|
||||
EmailVerified: identity.EmailVerified,
|
||||
Groups: identity.Groups,
|
||||
}
|
||||
|
||||
updater := func(a storage.AuthRequest) (storage.AuthRequest, error) {
|
||||
|
@ -501,22 +502,60 @@ func (s *Server) finalizeLogin(identity connector.Identity, authReq storage.Auth
|
|||
email = email + " (unverified)"
|
||||
}
|
||||
|
||||
s.logger.Infof("login successful: connector %q, username=%q, email=%q, groups=%q",
|
||||
authReq.ConnectorID, claims.Username, email, claims.Groups)
|
||||
s.logger.Infof("login successful: connector %q, username=%q, preferred_username=%q, email=%q, groups=%q",
|
||||
authReq.ConnectorID, claims.Username, claims.PreferredUsername, email, claims.Groups)
|
||||
|
||||
return path.Join(s.issuerURL.Path, "/approval") + "?req=" + authReq.ID, nil
|
||||
returnURL := path.Join(s.issuerURL.Path, "/approval") + "?req=" + authReq.ID
|
||||
_, ok := conn.(connector.RefreshConnector)
|
||||
if !ok {
|
||||
return returnURL, nil
|
||||
}
|
||||
|
||||
// Try to retrieve an existing OfflineSession object for the corresponding user.
|
||||
if session, err := s.storage.GetOfflineSessions(identity.UserID, authReq.ConnectorID); err != nil {
|
||||
if err != storage.ErrNotFound {
|
||||
s.logger.Errorf("failed to get offline session: %v", err)
|
||||
return "", err
|
||||
}
|
||||
offlineSessions := storage.OfflineSessions{
|
||||
UserID: identity.UserID,
|
||||
ConnID: authReq.ConnectorID,
|
||||
Refresh: make(map[string]*storage.RefreshTokenRef),
|
||||
ConnectorData: identity.ConnectorData,
|
||||
}
|
||||
|
||||
// Create a new OfflineSession object for the user and add a reference object for
|
||||
// the newly received refreshtoken.
|
||||
if err := s.storage.CreateOfflineSessions(offlineSessions); err != nil {
|
||||
s.logger.Errorf("failed to create offline session: %v", err)
|
||||
return "", err
|
||||
}
|
||||
} else {
|
||||
// Update existing OfflineSession obj with new RefreshTokenRef.
|
||||
if err := s.storage.UpdateOfflineSessions(session.UserID, session.ConnID, func(old storage.OfflineSessions) (storage.OfflineSessions, error) {
|
||||
if len(identity.ConnectorData) > 0 {
|
||||
old.ConnectorData = identity.ConnectorData
|
||||
}
|
||||
return old, nil
|
||||
}); err != nil {
|
||||
s.logger.Errorf("failed to update offline session: %v", err)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return returnURL, nil
|
||||
}
|
||||
|
||||
func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
|
||||
authReq, err := s.storage.GetAuthRequest(r.FormValue("req"))
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to get auth request: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Database error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Database error.")
|
||||
return
|
||||
}
|
||||
if !authReq.LoggedIn {
|
||||
s.logger.Errorf("Auth request does not have an identity for approval")
|
||||
s.renderError(w, http.StatusInternalServerError, "Login process not yet finalized.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Login process not yet finalized.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -529,15 +568,15 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
|
|||
client, err := s.storage.GetClient(authReq.ClientID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("Failed to get client %q: %v", authReq.ClientID, err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Failed to retrieve client.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Failed to retrieve client.")
|
||||
return
|
||||
}
|
||||
if err := s.templates.approval(w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes); err != nil {
|
||||
if err := s.templates.approval(r, w, authReq.ID, authReq.Claims.Username, client.Name, authReq.Scopes, r.URL.Path); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
case http.MethodPost:
|
||||
if r.FormValue("approval") != "approve" {
|
||||
s.renderError(w, http.StatusInternalServerError, "Approval rejected.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Approval rejected.")
|
||||
return
|
||||
}
|
||||
s.sendCodeResponse(w, r, authReq)
|
||||
|
@ -546,22 +585,22 @@ func (s *Server) handleApproval(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authReq storage.AuthRequest) {
|
||||
if s.now().After(authReq.Expiry) {
|
||||
s.renderError(w, http.StatusBadRequest, "User session has expired.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "User session has expired.")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.storage.DeleteAuthRequest(authReq.ID); err != nil {
|
||||
if err != storage.ErrNotFound {
|
||||
s.logger.Errorf("Failed to delete authorization request: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Internal server error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
|
||||
} else {
|
||||
s.renderError(w, http.StatusBadRequest, "User session error.")
|
||||
s.renderError(r, w, http.StatusBadRequest, "User session error.")
|
||||
}
|
||||
return
|
||||
}
|
||||
u, err := url.Parse(authReq.RedirectURI)
|
||||
if err != nil {
|
||||
s.renderError(w, http.StatusInternalServerError, "Invalid redirect URI.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Invalid redirect URI.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -598,14 +637,14 @@ func (s *Server) sendCodeResponse(w http.ResponseWriter, r *http.Request, authRe
|
|||
}
|
||||
if err := s.storage.CreateAuthCode(code); err != nil {
|
||||
s.logger.Errorf("Failed to create auth code: %v", err)
|
||||
s.renderError(w, http.StatusInternalServerError, "Internal server error.")
|
||||
s.renderError(r, w, http.StatusInternalServerError, "Internal server error.")
|
||||
return
|
||||
}
|
||||
|
||||
// Implicit and hybrid flows that try to use the OOB redirect URI are
|
||||
// rejected earlier. If we got here we're using the code flow.
|
||||
if authReq.RedirectURI == redirectURIOOB {
|
||||
if err := s.templates.oob(w, code.ID); err != nil {
|
||||
if err := s.templates.oob(r, w, code.ID, r.URL.Path); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
return
|
||||
|
@ -883,7 +922,6 @@ func (s *Server) handleAuthCode(w http.ResponseWriter, r *http.Request, client s
|
|||
deleteToken = true
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
s.writeAccessToken(w, idToken, accessToken, refreshToken, expiry)
|
||||
|
@ -961,6 +999,19 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
|
|||
scopes = requestedScopes
|
||||
}
|
||||
|
||||
var connectorData []byte
|
||||
if session, err := s.storage.GetOfflineSessions(refresh.Claims.UserID, refresh.ConnectorID); err != nil {
|
||||
if err != storage.ErrNotFound {
|
||||
s.logger.Errorf("failed to get offline session: %v", err)
|
||||
return
|
||||
}
|
||||
} else if len(refresh.ConnectorData) > 0 {
|
||||
// Use the old connector data if it exists, should be deleted once used
|
||||
connectorData = refresh.ConnectorData
|
||||
} else {
|
||||
connectorData = session.ConnectorData
|
||||
}
|
||||
|
||||
conn, err := s.getConnector(refresh.ConnectorID)
|
||||
if err != nil {
|
||||
s.logger.Errorf("connector with ID %q not found: %v", refresh.ConnectorID, err)
|
||||
|
@ -968,12 +1019,13 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
|
|||
return
|
||||
}
|
||||
ident := connector.Identity{
|
||||
UserID: refresh.Claims.UserID,
|
||||
Username: refresh.Claims.Username,
|
||||
Email: refresh.Claims.Email,
|
||||
EmailVerified: refresh.Claims.EmailVerified,
|
||||
Groups: refresh.Claims.Groups,
|
||||
ConnectorData: refresh.ConnectorData,
|
||||
UserID: refresh.Claims.UserID,
|
||||
Username: refresh.Claims.Username,
|
||||
PreferredUsername: refresh.Claims.PreferredUsername,
|
||||
Email: refresh.Claims.Email,
|
||||
EmailVerified: refresh.Claims.EmailVerified,
|
||||
Groups: refresh.Claims.Groups,
|
||||
ConnectorData: connectorData,
|
||||
}
|
||||
|
||||
// Can the connector refresh the identity? If so, attempt to refresh the data
|
||||
|
@ -992,11 +1044,12 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
|
|||
}
|
||||
|
||||
claims := storage.Claims{
|
||||
UserID: ident.UserID,
|
||||
Username: ident.Username,
|
||||
Email: ident.Email,
|
||||
EmailVerified: ident.EmailVerified,
|
||||
Groups: ident.Groups,
|
||||
UserID: ident.UserID,
|
||||
Username: ident.Username,
|
||||
PreferredUsername: ident.PreferredUsername,
|
||||
Email: ident.Email,
|
||||
EmailVerified: ident.EmailVerified,
|
||||
Groups: ident.Groups,
|
||||
}
|
||||
|
||||
accessToken, err := s.newAccessToken(client.ID, claims, scopes, refresh.Nonce, refresh.ConnectorID)
|
||||
|
@ -1034,11 +1087,14 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
|
|||
//
|
||||
// UserID intentionally ignored for now.
|
||||
old.Claims.Username = ident.Username
|
||||
old.Claims.PreferredUsername = ident.PreferredUsername
|
||||
old.Claims.Email = ident.Email
|
||||
old.Claims.EmailVerified = ident.EmailVerified
|
||||
old.Claims.Groups = ident.Groups
|
||||
old.ConnectorData = ident.ConnectorData
|
||||
old.LastUsed = lastUsed
|
||||
|
||||
// ConnectorData has been moved to OfflineSession
|
||||
old.ConnectorData = []byte{}
|
||||
return old, nil
|
||||
}
|
||||
|
||||
|
@ -1049,6 +1105,7 @@ func (s *Server) handleRefreshToken(w http.ResponseWriter, r *http.Request, clie
|
|||
return old, errors.New("refresh token invalid")
|
||||
}
|
||||
old.Refresh[refresh.ClientID].LastUsed = lastUsed
|
||||
old.ConnectorData = ident.ConnectorData
|
||||
return old, nil
|
||||
}); err != nil {
|
||||
s.logger.Errorf("failed to update offline session: %v", err)
|
||||
|
@ -1119,8 +1176,8 @@ func (s *Server) writeAccessToken(w http.ResponseWriter, idToken, accessToken, r
|
|||
w.Write(data)
|
||||
}
|
||||
|
||||
func (s *Server) renderError(w http.ResponseWriter, status int, description string) {
|
||||
if err := s.templates.err(w, status, description); err != nil {
|
||||
func (s *Server) renderError(r *http.Request, w http.ResponseWriter, status int, description string) {
|
||||
if err := s.templates.err(r, w, status, description); err != nil {
|
||||
s.logger.Errorf("Server template error: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ func TestHandleHealth(t *testing.T) {
|
|||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("expected 200 got %d", rr.Code)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type badStorage struct {
|
||||
|
|
|
@ -258,7 +258,8 @@ type idTokenClaims struct {
|
|||
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
|
||||
Name string `json:"name,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
PreferredUsername string `json:"preferred_username,omitempty"`
|
||||
|
||||
FederatedIDClaims *federatedIDClaims `json:"federated_claims,omitempty"`
|
||||
}
|
||||
|
@ -329,6 +330,7 @@ func (s *Server) newIDToken(clientID string, claims storage.Claims, scopes []str
|
|||
tok.Groups = claims.Groups
|
||||
case scope == scopeProfile:
|
||||
tok.Name = claims.Username
|
||||
tok.PreferredUsername = claims.PreferredUsername
|
||||
case scope == scopeFederatedID:
|
||||
tok.FederatedIDClaims = &federatedIDClaims{
|
||||
ConnectorID: connID,
|
||||
|
|
|
@ -14,6 +14,10 @@ import (
|
|||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/felixge/httpsnoop"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/dexidp/dex/connector"
|
||||
|
@ -21,6 +25,7 @@ import (
|
|||
"github.com/dexidp/dex/connector/bitbucketcloud"
|
||||
"github.com/dexidp/dex/connector/github"
|
||||
"github.com/dexidp/dex/connector/gitlab"
|
||||
"github.com/dexidp/dex/connector/google"
|
||||
"github.com/dexidp/dex/connector/keystone"
|
||||
"github.com/dexidp/dex/connector/ldap"
|
||||
"github.com/dexidp/dex/connector/linkedin"
|
||||
|
@ -30,10 +35,6 @@ import (
|
|||
"github.com/dexidp/dex/connector/saml"
|
||||
"github.com/dexidp/dex/pkg/log"
|
||||
"github.com/dexidp/dex/storage"
|
||||
"github.com/felixge/httpsnoop"
|
||||
"github.com/gorilla/handlers"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// LocalConnector is the local passwordDB connector which is an internal
|
||||
|
@ -453,6 +454,7 @@ var ConnectorsConfig = map[string]func() ConnectorConfig{
|
|||
"ldap": func() ConnectorConfig { return new(ldap.Config) },
|
||||
"github": func() ConnectorConfig { return new(github.Config) },
|
||||
"gitlab": func() ConnectorConfig { return new(gitlab.Config) },
|
||||
"google": func() ConnectorConfig { return new(google.Config) },
|
||||
"oidc": func() ConnectorConfig { return new(oidc.Config) },
|
||||
"saml": func() ConnectorConfig { return new(saml.Config) },
|
||||
"authproxy": func() ConnectorConfig { return new(authproxy.Config) },
|
||||
|
|
|
@ -590,6 +590,8 @@ func TestOAuth2CodeFlow(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if reqDump, err = httputil.DumpRequest(resp.Request, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -726,6 +728,8 @@ func TestOAuth2ImplicitFlow(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if reqDump, err = httputil.DumpRequest(resp.Request, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -799,7 +803,6 @@ func TestCrossClientScopes(t *testing.T) {
|
|||
if !reflect.DeepEqual(idToken.Audience, expAudience) {
|
||||
t.Errorf("expected audience %q, got %q", expAudience, idToken.Audience)
|
||||
}
|
||||
|
||||
}
|
||||
if gotState := q.Get("state"); gotState != state {
|
||||
t.Errorf("state did not match, want=%q got=%q", state, gotState)
|
||||
|
@ -848,6 +851,8 @@ func TestCrossClientScopes(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if reqDump, err = httputil.DumpRequest(resp.Request, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -921,7 +926,6 @@ func TestCrossClientScopesWithAzpInAudienceByDefault(t *testing.T) {
|
|||
if !reflect.DeepEqual(idToken.Audience, expAudience) {
|
||||
t.Errorf("expected audience %q, got %q", expAudience, idToken.Audience)
|
||||
}
|
||||
|
||||
}
|
||||
if gotState := q.Get("state"); gotState != state {
|
||||
t.Errorf("state did not match, want=%q got=%q", state, gotState)
|
||||
|
@ -969,6 +973,8 @@ func TestCrossClientScopesWithAzpInAudienceByDefault(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if reqDump, err = httputil.DumpRequest(resp.Request, false); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -1058,7 +1064,6 @@ func TestPasswordDB(t *testing.T) {
|
|||
t.Errorf("%s: %s", tc.name, diff)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestPasswordDBUsernamePrompt(t *testing.T) {
|
||||
|
@ -1225,9 +1230,11 @@ func TestRefreshTokenFlow(t *testing.T) {
|
|||
RedirectURL: redirectURL,
|
||||
}
|
||||
|
||||
if _, err = http.Get(oauth2Client.server.URL + "/login"); err != nil {
|
||||
resp, err := http.Get(oauth2Client.server.URL + "/login")
|
||||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
tok := &oauth2.Token{
|
||||
RefreshToken: oauth2Client.token.RefreshToken,
|
||||
|
@ -1235,9 +1242,11 @@ func TestRefreshTokenFlow(t *testing.T) {
|
|||
}
|
||||
|
||||
// Login in again to receive a new token.
|
||||
if _, err = http.Get(oauth2Client.server.URL + "/login"); err != nil {
|
||||
resp, err = http.Get(oauth2Client.server.URL + "/login")
|
||||
if err != nil {
|
||||
t.Fatalf("get failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// try to refresh expired token with old refresh token.
|
||||
if _, err := oauth2Client.config.TokenSource(ctx, tok).Token(); err == nil {
|
||||
|
|
|
@ -6,7 +6,9 @@ import (
|
|||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -45,19 +47,6 @@ type webConfig struct {
|
|||
extra map[string]string
|
||||
}
|
||||
|
||||
func join(base, path string) string {
|
||||
b := strings.HasSuffix(base, "/")
|
||||
p := strings.HasPrefix(path, "/")
|
||||
switch {
|
||||
case b && p:
|
||||
return base + path[1:]
|
||||
case b || p:
|
||||
return base + path
|
||||
default:
|
||||
return base + "/" + path
|
||||
}
|
||||
}
|
||||
|
||||
func dirExists(dir string) error {
|
||||
stat, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
|
@ -94,7 +83,7 @@ func loadWebConfig(c webConfig) (static, theme http.Handler, templates *template
|
|||
c.dir = "./web"
|
||||
}
|
||||
if c.logoURL == "" {
|
||||
c.logoURL = join(c.issuerURL, "theme/logo.png")
|
||||
c.logoURL = "theme/logo.png"
|
||||
}
|
||||
|
||||
if err := dirExists(c.dir); err != nil {
|
||||
|
@ -136,10 +125,15 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
|
|||
return nil, fmt.Errorf("no files in template dir %q", templatesDir)
|
||||
}
|
||||
|
||||
issuerURL, err := url.Parse(c.issuerURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing issuerURL: %v", err)
|
||||
}
|
||||
|
||||
funcs := map[string]interface{}{
|
||||
"issuer": func() string { return c.issuer },
|
||||
"logo": func() string { return c.logoURL },
|
||||
"url": func(s string) string { return join(c.issuerURL, s) },
|
||||
"url": func(reqPath, assetPath string) string { return relativeURL(issuerURL.Path, reqPath, assetPath) },
|
||||
"lower": strings.ToLower,
|
||||
"extra": func(k string) string { return c.extra[k] },
|
||||
}
|
||||
|
@ -166,6 +160,69 @@ func loadTemplates(c webConfig, templatesDir string) (*templates, error) {
|
|||
}, nil
|
||||
}
|
||||
|
||||
// relativeURL returns the URL of the asset relative to the URL of the request path.
|
||||
// The serverPath is consulted to trim any prefix due in case it is not listening
|
||||
// to the root path.
|
||||
//
|
||||
// Algorithm:
|
||||
// 1. Remove common prefix of serverPath and reqPath
|
||||
// 2. Remove common prefix of assetPath and reqPath
|
||||
// 3. For each part of reqPath remaining(minus one), go up one level (..)
|
||||
// 4. For each part of assetPath remaining, append it to result
|
||||
//
|
||||
//eg
|
||||
//server listens at localhost/dex so serverPath is dex
|
||||
//reqPath is /dex/auth
|
||||
//assetPath is static/main.css
|
||||
//relativeURL("/dex", "/dex/auth", "static/main.css") = "../static/main.css"
|
||||
func relativeURL(serverPath, reqPath, assetPath string) string {
|
||||
splitPath := func(p string) []string {
|
||||
res := []string{}
|
||||
parts := strings.Split(path.Clean(p), "/")
|
||||
for _, part := range parts {
|
||||
if part != "" {
|
||||
res = append(res, part)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
stripCommonParts := func(s1, s2 []string) ([]string, []string) {
|
||||
min := len(s1)
|
||||
if len(s2) < min {
|
||||
min = len(s2)
|
||||
}
|
||||
|
||||
splitIndex := min
|
||||
for i := 0; i < min; i++ {
|
||||
if s1[i] != s2[i] {
|
||||
splitIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return s1[splitIndex:], s2[splitIndex:]
|
||||
}
|
||||
|
||||
server, req, asset := splitPath(serverPath), splitPath(reqPath), splitPath(assetPath)
|
||||
|
||||
// Remove common prefix of request path with server path
|
||||
// nolint: ineffassign
|
||||
server, req = stripCommonParts(server, req)
|
||||
|
||||
// Remove common prefix of request path with asset path
|
||||
asset, req = stripCommonParts(asset, req)
|
||||
|
||||
// For each part of the request remaining (minus one) -> go up one level (..)
|
||||
// For each part of the asset remaining -> append it
|
||||
var relativeURL string
|
||||
for i := 0; i < len(req)-1; i++ {
|
||||
relativeURL = path.Join("..", relativeURL)
|
||||
}
|
||||
relativeURL = path.Join(relativeURL, path.Join(asset...))
|
||||
|
||||
return relativeURL
|
||||
}
|
||||
|
||||
var scopeDescriptions = map[string]string{
|
||||
"offline_access": "Have offline access",
|
||||
"profile": "View basic profile information",
|
||||
|
@ -184,26 +241,28 @@ func (n byName) Len() int { return len(n) }
|
|||
func (n byName) Less(i, j int) bool { return n[i].Name < n[j].Name }
|
||||
func (n byName) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
|
||||
|
||||
func (t *templates) login(w http.ResponseWriter, connectors []connectorInfo) error {
|
||||
func (t *templates) login(r *http.Request, w http.ResponseWriter, connectors []connectorInfo, reqPath string) error {
|
||||
sort.Sort(byName(connectors))
|
||||
data := struct {
|
||||
Connectors []connectorInfo
|
||||
}{connectors}
|
||||
ReqPath string
|
||||
}{connectors, r.URL.Path}
|
||||
return renderTemplate(w, t.loginTmpl, data)
|
||||
}
|
||||
|
||||
func (t *templates) password(w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool) error {
|
||||
func (t *templates) password(r *http.Request, w http.ResponseWriter, postURL, lastUsername, usernamePrompt string, lastWasInvalid, showBacklink bool, reqPath string) error {
|
||||
data := struct {
|
||||
PostURL string
|
||||
BackLink bool
|
||||
Username string
|
||||
UsernamePrompt string
|
||||
Invalid bool
|
||||
}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid}
|
||||
ReqPath string
|
||||
}{postURL, showBacklink, lastUsername, usernamePrompt, lastWasInvalid, r.URL.Path}
|
||||
return renderTemplate(w, t.passwordTmpl, data)
|
||||
}
|
||||
|
||||
func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientName string, scopes []string) error {
|
||||
func (t *templates) approval(r *http.Request, w http.ResponseWriter, authReqID, username, clientName string, scopes []string, reqPath string) error {
|
||||
accesses := []string{}
|
||||
for _, scope := range scopes {
|
||||
access, ok := scopeDescriptions[scope]
|
||||
|
@ -217,23 +276,26 @@ func (t *templates) approval(w http.ResponseWriter, authReqID, username, clientN
|
|||
Client string
|
||||
AuthReqID string
|
||||
Scopes []string
|
||||
}{username, clientName, authReqID, accesses}
|
||||
ReqPath string
|
||||
}{username, clientName, authReqID, accesses, r.URL.Path}
|
||||
return renderTemplate(w, t.approvalTmpl, data)
|
||||
}
|
||||
|
||||
func (t *templates) oob(w http.ResponseWriter, code string) error {
|
||||
func (t *templates) oob(r *http.Request, w http.ResponseWriter, code string, reqPath string) error {
|
||||
data := struct {
|
||||
Code string
|
||||
}{code}
|
||||
Code string
|
||||
ReqPath string
|
||||
}{code, r.URL.Path}
|
||||
return renderTemplate(w, t.oobTmpl, data)
|
||||
}
|
||||
|
||||
func (t *templates) err(w http.ResponseWriter, errCode int, errMsg string) error {
|
||||
func (t *templates) err(r *http.Request, w http.ResponseWriter, errCode int, errMsg string) error {
|
||||
w.WriteHeader(errCode)
|
||||
data := struct {
|
||||
ErrType string
|
||||
ErrMsg string
|
||||
}{http.StatusText(errCode), errMsg}
|
||||
ReqPath string
|
||||
}{http.StatusText(errCode), errMsg, r.URL.Path}
|
||||
if err := t.errorTmpl.Execute(w, data); err != nil {
|
||||
return fmt.Errorf("Error rendering template %s: %s", t.errorTmpl.Name(), err)
|
||||
}
|
||||
|
|
|
@ -1 +1,44 @@
|
|||
package server
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestRelativeURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
serverPath string
|
||||
reqPath string
|
||||
assetPath string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "server-root-req-one-level-asset-two-level",
|
||||
serverPath: "/",
|
||||
reqPath: "/auth",
|
||||
assetPath: "/theme/main.css",
|
||||
expected: "theme/main.css",
|
||||
},
|
||||
{
|
||||
name: "server-one-level-req-one-level-asset-two-level",
|
||||
serverPath: "/dex",
|
||||
reqPath: "/dex/auth",
|
||||
assetPath: "/theme/main.css",
|
||||
expected: "theme/main.css",
|
||||
},
|
||||
{
|
||||
name: "server-root-req-two-level-asset-three-level",
|
||||
serverPath: "/dex",
|
||||
reqPath: "/dex/auth/connector",
|
||||
assetPath: "assets/css/main.css",
|
||||
expected: "../assets/css/main.css",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := relativeURL(test.serverPath, test.reqPath, test.assetPath)
|
||||
if actual != test.expected {
|
||||
t.Fatalf("Got '%s'. Expected '%s'", actual, test.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -511,15 +511,15 @@ func testPasswordCRUD(t *testing.T, s storage.Storage) {
|
|||
|
||||
_, err = s.GetPassword(password1.Email)
|
||||
mustBeErrNotFound(t, "password", err)
|
||||
|
||||
}
|
||||
|
||||
func testOfflineSessionCRUD(t *testing.T, s storage.Storage) {
|
||||
userID1 := storage.NewID()
|
||||
session1 := storage.OfflineSessions{
|
||||
UserID: userID1,
|
||||
ConnID: "Conn1",
|
||||
Refresh: make(map[string]*storage.RefreshTokenRef),
|
||||
UserID: userID1,
|
||||
ConnID: "Conn1",
|
||||
Refresh: make(map[string]*storage.RefreshTokenRef),
|
||||
ConnectorData: []byte(`{"some":"data"}`),
|
||||
}
|
||||
|
||||
// Creating an OfflineSession with an empty Refresh list to ensure that
|
||||
|
@ -534,9 +534,10 @@ func testOfflineSessionCRUD(t *testing.T, s storage.Storage) {
|
|||
|
||||
userID2 := storage.NewID()
|
||||
session2 := storage.OfflineSessions{
|
||||
UserID: userID2,
|
||||
ConnID: "Conn2",
|
||||
Refresh: make(map[string]*storage.RefreshTokenRef),
|
||||
UserID: userID2,
|
||||
ConnID: "Conn2",
|
||||
Refresh: make(map[string]*storage.RefreshTokenRef),
|
||||
ConnectorData: []byte(`{"some":"data"}`),
|
||||
}
|
||||
|
||||
if err := s.CreateOfflineSessions(session2); err != nil {
|
||||
|
|
|
@ -156,7 +156,7 @@ func (c *conn) UpdateRefreshToken(id string, updater func(old storage.RefreshTok
|
|||
return c.txnUpdate(ctx, keyID(refreshTokenPrefix, id), func(currentValue []byte) ([]byte, error) {
|
||||
var current RefreshToken
|
||||
if len(currentValue) > 0 {
|
||||
if err := json.Unmarshal([]byte(currentValue), ¤t); err != nil {
|
||||
if err := json.Unmarshal(currentValue, ¤t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -148,30 +148,33 @@ func fromStorageRefreshToken(r storage.RefreshToken) RefreshToken {
|
|||
|
||||
// Claims is a mirrored struct from storage with JSON struct tags.
|
||||
type Claims struct {
|
||||
UserID string `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
UserID string `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
PreferredUsername string `json:"preferredUsername"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
func fromStorageClaims(i storage.Claims) Claims {
|
||||
return Claims{
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
PreferredUsername: i.PreferredUsername,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
}
|
||||
}
|
||||
|
||||
func toStorageClaims(i Claims) storage.Claims {
|
||||
return storage.Claims{
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
PreferredUsername: i.PreferredUsername,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -185,24 +188,27 @@ type Keys struct {
|
|||
|
||||
// OfflineSessions is a mirrored struct from storage with JSON struct tags
|
||||
type OfflineSessions struct {
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
ConnID string `json:"conn_id,omitempty"`
|
||||
Refresh map[string]*storage.RefreshTokenRef `json:"refresh,omitempty"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
ConnID string `json:"conn_id,omitempty"`
|
||||
Refresh map[string]*storage.RefreshTokenRef `json:"refresh,omitempty"`
|
||||
ConnectorData []byte `json:"connectorData,omitempty"`
|
||||
}
|
||||
|
||||
func fromStorageOfflineSessions(o storage.OfflineSessions) OfflineSessions {
|
||||
return OfflineSessions{
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
ConnectorData: o.ConnectorData,
|
||||
}
|
||||
}
|
||||
|
||||
func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions {
|
||||
s := storage.OfflineSessions{
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
ConnectorData: o.ConnectorData,
|
||||
}
|
||||
if s.Refresh == nil {
|
||||
// Server code assumes this will be non-nil.
|
||||
|
|
|
@ -55,14 +55,14 @@ type client struct {
|
|||
}
|
||||
|
||||
// idToName maps an arbitrary ID, such as an email or client ID to a Kubernetes object name.
|
||||
func (c *client) idToName(s string) string {
|
||||
return idToName(s, c.hash)
|
||||
func (cli *client) idToName(s string) string {
|
||||
return idToName(s, cli.hash)
|
||||
}
|
||||
|
||||
// offlineTokenName maps two arbitrary IDs, to a single Kubernetes object name.
|
||||
// This is used when more than one field is used to uniquely identify the object.
|
||||
func (c *client) offlineTokenName(userID string, connID string) string {
|
||||
return offlineTokenName(userID, connID, c.hash)
|
||||
func (cli *client) offlineTokenName(userID string, connID string) string {
|
||||
return offlineTokenName(userID, connID, cli.hash)
|
||||
}
|
||||
|
||||
// Kubernetes names must match the regexp '[a-z0-9]([-a-z0-9]*[a-z0-9])?'.
|
||||
|
@ -79,7 +79,7 @@ func offlineTokenName(userID string, connID string, h func() hash.Hash) string {
|
|||
return strings.TrimRight(encoding.EncodeToString(hash.Sum(nil)), "=")
|
||||
}
|
||||
|
||||
func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
|
||||
func (cli *client) urlFor(apiVersion, namespace, resource, name string) string {
|
||||
basePath := "apis/"
|
||||
if apiVersion == "v1" {
|
||||
basePath = "api/"
|
||||
|
@ -91,10 +91,10 @@ func (c *client) urlFor(apiVersion, namespace, resource, name string) string {
|
|||
} else {
|
||||
p = path.Join(basePath, apiVersion, resource, name)
|
||||
}
|
||||
if strings.HasSuffix(c.baseURL, "/") {
|
||||
return c.baseURL + p
|
||||
if strings.HasSuffix(cli.baseURL, "/") {
|
||||
return cli.baseURL + p
|
||||
}
|
||||
return c.baseURL + "/" + p
|
||||
return cli.baseURL + "/" + p
|
||||
}
|
||||
|
||||
// Define an error interface so we can get at the underlying status code if it's
|
||||
|
@ -156,13 +156,13 @@ func closeResp(r *http.Response) {
|
|||
r.Body.Close()
|
||||
}
|
||||
|
||||
func (c *client) get(resource, name string, v interface{}) error {
|
||||
return c.getResource(c.apiVersion, c.namespace, resource, name, v)
|
||||
func (cli *client) get(resource, name string, v interface{}) error {
|
||||
return cli.getResource(cli.apiVersion, cli.namespace, resource, name, v)
|
||||
}
|
||||
|
||||
func (c *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error {
|
||||
url := c.urlFor(apiVersion, namespace, resource, name)
|
||||
resp, err := c.client.Get(url)
|
||||
func (cli *client) getResource(apiVersion, namespace, resource, name string, v interface{}) error {
|
||||
url := cli.urlFor(apiVersion, namespace, resource, name)
|
||||
resp, err := cli.client.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -173,22 +173,22 @@ func (c *client) getResource(apiVersion, namespace, resource, name string, v int
|
|||
return json.NewDecoder(resp.Body).Decode(v)
|
||||
}
|
||||
|
||||
func (c *client) list(resource string, v interface{}) error {
|
||||
return c.get(resource, "", v)
|
||||
func (cli *client) list(resource string, v interface{}) error {
|
||||
return cli.get(resource, "", v)
|
||||
}
|
||||
|
||||
func (c *client) post(resource string, v interface{}) error {
|
||||
return c.postResource(c.apiVersion, c.namespace, resource, v)
|
||||
func (cli *client) post(resource string, v interface{}) error {
|
||||
return cli.postResource(cli.apiVersion, cli.namespace, resource, v)
|
||||
}
|
||||
|
||||
func (c *client) postResource(apiVersion, namespace, resource string, v interface{}) error {
|
||||
func (cli *client) postResource(apiVersion, namespace, resource string, v interface{}) error {
|
||||
body, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal object: %v", err)
|
||||
}
|
||||
|
||||
url := c.urlFor(apiVersion, namespace, resource, "")
|
||||
resp, err := c.client.Post(url, "application/json", bytes.NewReader(body))
|
||||
url := cli.urlFor(apiVersion, namespace, resource, "")
|
||||
resp, err := cli.client.Post(url, "application/json", bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -196,13 +196,13 @@ func (c *client) postResource(apiVersion, namespace, resource string, v interfac
|
|||
return checkHTTPErr(resp, http.StatusCreated)
|
||||
}
|
||||
|
||||
func (c *client) delete(resource, name string) error {
|
||||
url := c.urlFor(c.apiVersion, c.namespace, resource, name)
|
||||
func (cli *client) delete(resource, name string) error {
|
||||
url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("create delete request: %v", err)
|
||||
}
|
||||
resp, err := c.client.Do(req)
|
||||
resp, err := cli.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("delete request: %v", err)
|
||||
}
|
||||
|
@ -210,7 +210,7 @@ func (c *client) delete(resource, name string) error {
|
|||
return checkHTTPErr(resp, http.StatusOK)
|
||||
}
|
||||
|
||||
func (c *client) deleteAll(resource string) error {
|
||||
func (cli *client) deleteAll(resource string) error {
|
||||
var list struct {
|
||||
k8sapi.TypeMeta `json:",inline"`
|
||||
k8sapi.ListMeta `json:"metadata,omitempty"`
|
||||
|
@ -219,24 +219,24 @@ func (c *client) deleteAll(resource string) error {
|
|||
k8sapi.ObjectMeta `json:"metadata,omitempty"`
|
||||
} `json:"items"`
|
||||
}
|
||||
if err := c.list(resource, &list); err != nil {
|
||||
if err := cli.list(resource, &list); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, item := range list.Items {
|
||||
if err := c.delete(resource, item.Name); err != nil {
|
||||
if err := cli.delete(resource, item.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *client) put(resource, name string, v interface{}) error {
|
||||
func (cli *client) put(resource, name string, v interface{}) error {
|
||||
body, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return fmt.Errorf("marshal object: %v", err)
|
||||
}
|
||||
|
||||
url := c.urlFor(c.apiVersion, c.namespace, resource, name)
|
||||
url := cli.urlFor(cli.apiVersion, cli.namespace, resource, name)
|
||||
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
|
||||
if err != nil {
|
||||
return fmt.Errorf("create patch request: %v", err)
|
||||
|
@ -244,7 +244,7 @@ func (c *client) put(resource, name string, v interface{}) error {
|
|||
|
||||
req.Header.Set("Content-Length", strconv.Itoa(len(body)))
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
resp, err := cli.client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("patch request: %v", err)
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ type CustomResourceDefinitionNames struct {
|
|||
ListKind string `json:"listKind,omitempty" protobuf:"bytes,5,opt,name=listKind"`
|
||||
}
|
||||
|
||||
// ResourceScope is an enum defining the different scopes availabe to a custom resource
|
||||
// ResourceScope is an enum defining the different scopes available to a custom resource
|
||||
type ResourceScope string
|
||||
|
||||
const (
|
||||
|
|
|
@ -210,30 +210,33 @@ func toStorageClient(c Client) storage.Client {
|
|||
|
||||
// Claims is a mirrored struct from storage with JSON struct tags.
|
||||
type Claims struct {
|
||||
UserID string `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
UserID string `json:"userID"`
|
||||
Username string `json:"username"`
|
||||
PreferredUsername string `json:"preferredUsername"`
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
Groups []string `json:"groups,omitempty"`
|
||||
}
|
||||
|
||||
func fromStorageClaims(i storage.Claims) Claims {
|
||||
return Claims{
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
PreferredUsername: i.PreferredUsername,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
}
|
||||
}
|
||||
|
||||
func toStorageClaims(i Claims) storage.Claims {
|
||||
return storage.Claims{
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
UserID: i.UserID,
|
||||
Username: i.Username,
|
||||
PreferredUsername: i.PreferredUsername,
|
||||
Email: i.Email,
|
||||
EmailVerified: i.EmailVerified,
|
||||
Groups: i.Groups,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -549,9 +552,10 @@ type OfflineSessions struct {
|
|||
k8sapi.TypeMeta `json:",inline"`
|
||||
k8sapi.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
UserID string `json:"userID,omitempty"`
|
||||
ConnID string `json:"connID,omitempty"`
|
||||
Refresh map[string]*storage.RefreshTokenRef `json:"refresh,omitempty"`
|
||||
UserID string `json:"userID,omitempty"`
|
||||
ConnID string `json:"connID,omitempty"`
|
||||
Refresh map[string]*storage.RefreshTokenRef `json:"refresh,omitempty"`
|
||||
ConnectorData []byte `json:"connectorData,omitempty"`
|
||||
}
|
||||
|
||||
func (cli *client) fromStorageOfflineSessions(o storage.OfflineSessions) OfflineSessions {
|
||||
|
@ -564,17 +568,19 @@ func (cli *client) fromStorageOfflineSessions(o storage.OfflineSessions) Offline
|
|||
Name: cli.offlineTokenName(o.UserID, o.ConnID),
|
||||
Namespace: cli.namespace,
|
||||
},
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
ConnectorData: o.ConnectorData,
|
||||
}
|
||||
}
|
||||
|
||||
func toStorageOfflineSessions(o OfflineSessions) storage.OfflineSessions {
|
||||
s := storage.OfflineSessions{
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
UserID: o.UserID,
|
||||
ConnID: o.ConnID,
|
||||
Refresh: o.Refresh,
|
||||
ConnectorData: o.ConnectorData,
|
||||
}
|
||||
if s.Refresh == nil {
|
||||
// Server code assumes this will be non-nil.
|
||||
|
|
|
@ -29,6 +29,7 @@ const (
|
|||
// MySQL error codes
|
||||
mysqlErrDupEntry = 1062
|
||||
mysqlErrDupEntryWithKeyName = 1586
|
||||
mysqlErrUnknownSysVar = 1193
|
||||
)
|
||||
|
||||
// SQLite3 options for creating an SQL db.
|
||||
|
@ -307,6 +308,26 @@ func (s *MySQL) open(logger log.Logger) (*conn, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
err = db.Ping()
|
||||
if err != nil {
|
||||
if mysqlErr, ok := err.(*mysql.MySQLError); ok && mysqlErr.Number == mysqlErrUnknownSysVar {
|
||||
logger.Info("reconnecting with MySQL pre-5.7.20 compatibility mode")
|
||||
|
||||
// MySQL 5.7.20 introduced transaction_isolation and deprecated tx_isolation.
|
||||
// MySQL 8.0 doesn't have tx_isolation at all.
|
||||
// https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_transaction_isolation
|
||||
delete(cfg.Params, "transaction_isolation")
|
||||
cfg.Params["tx_isolation"] = "'SERIALIZABLE'"
|
||||
|
||||
db, err = sql.Open("mysql", cfg.FormatDSN())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
errCheck := func(err error) bool {
|
||||
sqlErr, ok := err.(*mysql.MySQLError)
|
||||
if !ok {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -220,12 +221,24 @@ func TestPostgres(t *testing.T) {
|
|||
if host == "" {
|
||||
t.Skipf("test environment variable %q not set, skipping", testPostgresEnv)
|
||||
}
|
||||
|
||||
port := uint64(5432)
|
||||
if rawPort := os.Getenv("DEX_POSTGRES_PORT"); rawPort != "" {
|
||||
var err error
|
||||
|
||||
port, err = strconv.ParseUint(rawPort, 10, 32)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid postgres port %q: %s", rawPort, err)
|
||||
}
|
||||
}
|
||||
|
||||
p := &Postgres{
|
||||
NetworkDB: NetworkDB{
|
||||
Database: getenv("DEX_POSTGRES_DATABASE", "postgres"),
|
||||
User: getenv("DEX_POSTGRES_USER", "postgres"),
|
||||
Password: getenv("DEX_POSTGRES_PASSWORD", "postgres"),
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
ConnectionTimeout: 5,
|
||||
},
|
||||
SSL: SSL{
|
||||
|
@ -242,12 +255,24 @@ func TestMySQL(t *testing.T) {
|
|||
if host == "" {
|
||||
t.Skipf("test environment variable %q not set, skipping", testMySQLEnv)
|
||||
}
|
||||
|
||||
port := uint64(3306)
|
||||
if rawPort := os.Getenv("DEX_MYSQL_PORT"); rawPort != "" {
|
||||
var err error
|
||||
|
||||
port, err = strconv.ParseUint(rawPort, 10, 32)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid mysql port %q: %s", rawPort, err)
|
||||
}
|
||||
}
|
||||
|
||||
s := &MySQL{
|
||||
NetworkDB: NetworkDB{
|
||||
Database: getenv("DEX_MYSQL_DATABASE", "mysql"),
|
||||
User: getenv("DEX_MYSQL_USER", "mysql"),
|
||||
Password: getenv("DEX_MYSQL_PASSWORD", ""),
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
ConnectionTimeout: 5,
|
||||
},
|
||||
SSL: SSL{
|
||||
|
|
|
@ -108,19 +108,19 @@ func (c *conn) CreateAuthRequest(a storage.AuthRequest) error {
|
|||
insert into auth_request (
|
||||
id, client_id, response_types, scopes, redirect_uri, nonce, state,
|
||||
force_approval_prompt, logged_in,
|
||||
claims_user_id, claims_username, claims_email, claims_email_verified,
|
||||
claims_groups,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data,
|
||||
expiry
|
||||
)
|
||||
values (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17
|
||||
$1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18
|
||||
);
|
||||
`,
|
||||
a.ID, a.ClientID, encoder(a.ResponseTypes), encoder(a.Scopes), a.RedirectURI, a.Nonce, a.State,
|
||||
a.ForceApprovalPrompt, a.LoggedIn,
|
||||
a.Claims.UserID, a.Claims.Username, a.Claims.Email, a.Claims.EmailVerified,
|
||||
encoder(a.Claims.Groups),
|
||||
a.Claims.UserID, a.Claims.Username, a.Claims.PreferredUsername,
|
||||
a.Claims.Email, a.Claims.EmailVerified, encoder(a.Claims.Groups),
|
||||
a.ConnectorID, a.ConnectorData,
|
||||
a.Expiry,
|
||||
)
|
||||
|
@ -149,16 +149,17 @@ func (c *conn) UpdateAuthRequest(id string, updater func(a storage.AuthRequest)
|
|||
set
|
||||
client_id = $1, response_types = $2, scopes = $3, redirect_uri = $4,
|
||||
nonce = $5, state = $6, force_approval_prompt = $7, logged_in = $8,
|
||||
claims_user_id = $9, claims_username = $10, claims_email = $11,
|
||||
claims_email_verified = $12,
|
||||
claims_groups = $13,
|
||||
connector_id = $14, connector_data = $15,
|
||||
expiry = $16
|
||||
where id = $17;
|
||||
claims_user_id = $9, claims_username = $10, claims_preferred_username = $11,
|
||||
claims_email = $12, claims_email_verified = $13,
|
||||
claims_groups = $14,
|
||||
connector_id = $15, connector_data = $16,
|
||||
expiry = $17
|
||||
where id = $18;
|
||||
`,
|
||||
a.ClientID, encoder(a.ResponseTypes), encoder(a.Scopes), a.RedirectURI, a.Nonce, a.State,
|
||||
a.ForceApprovalPrompt, a.LoggedIn,
|
||||
a.Claims.UserID, a.Claims.Username, a.Claims.Email, a.Claims.EmailVerified,
|
||||
a.Claims.UserID, a.Claims.Username, a.Claims.PreferredUsername,
|
||||
a.Claims.Email, a.Claims.EmailVerified,
|
||||
encoder(a.Claims.Groups),
|
||||
a.ConnectorID, a.ConnectorData,
|
||||
a.Expiry, r.ID,
|
||||
|
@ -168,7 +169,6 @@ func (c *conn) UpdateAuthRequest(id string, updater func(a storage.AuthRequest)
|
|||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func (c *conn) GetAuthRequest(id string) (storage.AuthRequest, error) {
|
||||
|
@ -177,17 +177,18 @@ func (c *conn) GetAuthRequest(id string) (storage.AuthRequest, error) {
|
|||
|
||||
func getAuthRequest(q querier, id string) (a storage.AuthRequest, err error) {
|
||||
err = q.QueryRow(`
|
||||
select
|
||||
select
|
||||
id, client_id, response_types, scopes, redirect_uri, nonce, state,
|
||||
force_approval_prompt, logged_in,
|
||||
claims_user_id, claims_username, claims_email, claims_email_verified,
|
||||
claims_groups,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data, expiry
|
||||
from auth_request where id = $1;
|
||||
`, id).Scan(
|
||||
&a.ID, &a.ClientID, decoder(&a.ResponseTypes), decoder(&a.Scopes), &a.RedirectURI, &a.Nonce, &a.State,
|
||||
&a.ForceApprovalPrompt, &a.LoggedIn,
|
||||
&a.Claims.UserID, &a.Claims.Username, &a.Claims.Email, &a.Claims.EmailVerified,
|
||||
&a.Claims.UserID, &a.Claims.Username, &a.Claims.PreferredUsername,
|
||||
&a.Claims.Email, &a.Claims.EmailVerified,
|
||||
decoder(&a.Claims.Groups),
|
||||
&a.ConnectorID, &a.ConnectorData, &a.Expiry,
|
||||
)
|
||||
|
@ -204,16 +205,16 @@ func (c *conn) CreateAuthCode(a storage.AuthCode) error {
|
|||
_, err := c.Exec(`
|
||||
insert into auth_code (
|
||||
id, client_id, scopes, nonce, redirect_uri,
|
||||
claims_user_id, claims_username,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data,
|
||||
expiry
|
||||
)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13);
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);
|
||||
`,
|
||||
a.ID, a.ClientID, encoder(a.Scopes), a.Nonce, a.RedirectURI, a.Claims.UserID,
|
||||
a.Claims.Username, a.Claims.Email, a.Claims.EmailVerified, encoder(a.Claims.Groups),
|
||||
a.ConnectorID, a.ConnectorData, a.Expiry,
|
||||
a.Claims.Username, a.Claims.PreferredUsername, a.Claims.Email, a.Claims.EmailVerified,
|
||||
encoder(a.Claims.Groups), a.ConnectorID, a.ConnectorData, a.Expiry,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
@ -229,15 +230,15 @@ func (c *conn) GetAuthCode(id string) (a storage.AuthCode, err error) {
|
|||
err = c.QueryRow(`
|
||||
select
|
||||
id, client_id, scopes, nonce, redirect_uri,
|
||||
claims_user_id, claims_username,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data,
|
||||
expiry
|
||||
from auth_code where id = $1;
|
||||
`, id).Scan(
|
||||
&a.ID, &a.ClientID, decoder(&a.Scopes), &a.Nonce, &a.RedirectURI, &a.Claims.UserID,
|
||||
&a.Claims.Username, &a.Claims.Email, &a.Claims.EmailVerified, decoder(&a.Claims.Groups),
|
||||
&a.ConnectorID, &a.ConnectorData, &a.Expiry,
|
||||
&a.Claims.Username, &a.Claims.PreferredUsername, &a.Claims.Email, &a.Claims.EmailVerified,
|
||||
decoder(&a.Claims.Groups), &a.ConnectorID, &a.ConnectorData, &a.Expiry,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
@ -252,15 +253,16 @@ func (c *conn) CreateRefresh(r storage.RefreshToken) error {
|
|||
_, err := c.Exec(`
|
||||
insert into refresh_token (
|
||||
id, client_id, scopes, nonce,
|
||||
claims_user_id, claims_username, claims_email, claims_email_verified,
|
||||
claims_groups,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data,
|
||||
token, created_at, last_used
|
||||
)
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);
|
||||
values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15);
|
||||
`,
|
||||
r.ID, r.ClientID, encoder(r.Scopes), r.Nonce,
|
||||
r.Claims.UserID, r.Claims.Username, r.Claims.Email, r.Claims.EmailVerified,
|
||||
r.Claims.UserID, r.Claims.Username, r.Claims.PreferredUsername,
|
||||
r.Claims.Email, r.Claims.EmailVerified,
|
||||
encoder(r.Claims.Groups),
|
||||
r.ConnectorID, r.ConnectorData,
|
||||
r.Token, r.CreatedAt, r.LastUsed,
|
||||
|
@ -291,19 +293,21 @@ func (c *conn) UpdateRefreshToken(id string, updater func(old storage.RefreshTok
|
|||
nonce = $3,
|
||||
claims_user_id = $4,
|
||||
claims_username = $5,
|
||||
claims_email = $6,
|
||||
claims_email_verified = $7,
|
||||
claims_groups = $8,
|
||||
connector_id = $9,
|
||||
connector_data = $10,
|
||||
token = $11,
|
||||
created_at = $12,
|
||||
last_used = $13
|
||||
claims_preferred_username = $6,
|
||||
claims_email = $7,
|
||||
claims_email_verified = $8,
|
||||
claims_groups = $9,
|
||||
connector_id = $10,
|
||||
connector_data = $11,
|
||||
token = $12,
|
||||
created_at = $13,
|
||||
last_used = $14
|
||||
where
|
||||
id = $14
|
||||
id = $15
|
||||
`,
|
||||
r.ClientID, encoder(r.Scopes), r.Nonce,
|
||||
r.Claims.UserID, r.Claims.Username, r.Claims.Email, r.Claims.EmailVerified,
|
||||
r.Claims.UserID, r.Claims.Username, r.Claims.PreferredUsername,
|
||||
r.Claims.Email, r.Claims.EmailVerified,
|
||||
encoder(r.Claims.Groups),
|
||||
r.ConnectorID, r.ConnectorData,
|
||||
r.Token, r.CreatedAt, r.LastUsed, id,
|
||||
|
@ -323,7 +327,8 @@ func getRefresh(q querier, id string) (storage.RefreshToken, error) {
|
|||
return scanRefresh(q.QueryRow(`
|
||||
select
|
||||
id, client_id, scopes, nonce,
|
||||
claims_user_id, claims_username, claims_email, claims_email_verified,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified,
|
||||
claims_groups,
|
||||
connector_id, connector_data,
|
||||
token, created_at, last_used
|
||||
|
@ -335,8 +340,8 @@ func (c *conn) ListRefreshTokens() ([]storage.RefreshToken, error) {
|
|||
rows, err := c.Query(`
|
||||
select
|
||||
id, client_id, scopes, nonce,
|
||||
claims_user_id, claims_username, claims_email, claims_email_verified,
|
||||
claims_groups,
|
||||
claims_user_id, claims_username, claims_preferred_username,
|
||||
claims_email, claims_email_verified, claims_groups,
|
||||
connector_id, connector_data,
|
||||
token, created_at, last_used
|
||||
from refresh_token;
|
||||
|
@ -361,7 +366,8 @@ func (c *conn) ListRefreshTokens() ([]storage.RefreshToken, error) {
|
|||
func scanRefresh(s scanner) (r storage.RefreshToken, err error) {
|
||||
err = s.Scan(
|
||||
&r.ID, &r.ClientID, decoder(&r.Scopes), &r.Nonce,
|
||||
&r.Claims.UserID, &r.Claims.Username, &r.Claims.Email, &r.Claims.EmailVerified,
|
||||
&r.Claims.UserID, &r.Claims.Username, &r.Claims.PreferredUsername,
|
||||
&r.Claims.Email, &r.Claims.EmailVerified,
|
||||
decoder(&r.Claims.Groups),
|
||||
&r.ConnectorID, &r.ConnectorData,
|
||||
&r.Token, &r.CreatedAt, &r.LastUsed,
|
||||
|
@ -410,7 +416,7 @@ func (c *conn) UpdateKeys(updater func(old storage.Keys) (storage.Keys, error))
|
|||
} else {
|
||||
_, err = tx.Exec(`
|
||||
update keys
|
||||
set
|
||||
set
|
||||
verification_keys = $1,
|
||||
signing_key = $2,
|
||||
signing_key_pub = $3,
|
||||
|
@ -648,13 +654,13 @@ func scanPassword(s scanner) (p storage.Password, err error) {
|
|||
func (c *conn) CreateOfflineSessions(s storage.OfflineSessions) error {
|
||||
_, err := c.Exec(`
|
||||
insert into offline_session (
|
||||
user_id, conn_id, refresh
|
||||
user_id, conn_id, refresh, connector_data
|
||||
)
|
||||
values (
|
||||
$1, $2, $3
|
||||
$1, $2, $3, $4
|
||||
);
|
||||
`,
|
||||
s.UserID, s.ConnID, encoder(s.Refresh),
|
||||
s.UserID, s.ConnID, encoder(s.Refresh), s.ConnectorData,
|
||||
)
|
||||
if err != nil {
|
||||
if c.alreadyExistsCheck(err) {
|
||||
|
@ -679,10 +685,11 @@ func (c *conn) UpdateOfflineSessions(userID string, connID string, updater func(
|
|||
_, err = tx.Exec(`
|
||||
update offline_session
|
||||
set
|
||||
refresh = $1
|
||||
where user_id = $2 AND conn_id = $3;
|
||||
refresh = $1,
|
||||
connector_data = $2
|
||||
where user_id = $3 AND conn_id = $4;
|
||||
`,
|
||||
encoder(newSession.Refresh), s.UserID, s.ConnID,
|
||||
encoder(newSession.Refresh), newSession.ConnectorData, s.UserID, s.ConnID,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("update offline session: %v", err)
|
||||
|
@ -698,7 +705,7 @@ func (c *conn) GetOfflineSessions(userID string, connID string) (storage.Offline
|
|||
func getOfflineSessions(q querier, userID string, connID string) (storage.OfflineSessions, error) {
|
||||
return scanOfflineSessions(q.QueryRow(`
|
||||
select
|
||||
user_id, conn_id, refresh
|
||||
user_id, conn_id, refresh, connector_data
|
||||
from offline_session
|
||||
where user_id = $1 AND conn_id = $2;
|
||||
`, userID, connID))
|
||||
|
@ -706,7 +713,7 @@ func getOfflineSessions(q querier, userID string, connID string) (storage.Offlin
|
|||
|
||||
func scanOfflineSessions(s scanner) (o storage.OfflineSessions, err error) {
|
||||
err = s.Scan(
|
||||
&o.UserID, &o.ConnID, decoder(&o.Refresh),
|
||||
&o.UserID, &o.ConnID, decoder(&o.Refresh), &o.ConnectorData,
|
||||
)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
|
@ -750,7 +757,7 @@ func (c *conn) UpdateConnector(id string, updater func(s storage.Connector) (sto
|
|||
}
|
||||
_, err = tx.Exec(`
|
||||
update connector
|
||||
set
|
||||
set
|
||||
type = $1,
|
||||
name = $2,
|
||||
resource_version = $3,
|
||||
|
|
|
@ -90,18 +90,18 @@ var migrations = []migration{
|
|||
nonce text not null,
|
||||
state text not null,
|
||||
force_approval_prompt boolean not null,
|
||||
|
||||
|
||||
logged_in boolean not null,
|
||||
|
||||
|
||||
claims_user_id text not null,
|
||||
claims_username text not null,
|
||||
claims_email text not null,
|
||||
claims_email_verified boolean not null,
|
||||
claims_groups bytea not null, -- JSON array of strings
|
||||
|
||||
|
||||
connector_id text not null,
|
||||
connector_data bytea,
|
||||
|
||||
|
||||
expiry timestamptz not null
|
||||
);`,
|
||||
`
|
||||
|
@ -111,16 +111,16 @@ var migrations = []migration{
|
|||
scopes bytea not null, -- JSON array of strings
|
||||
nonce text not null,
|
||||
redirect_uri text not null,
|
||||
|
||||
|
||||
claims_user_id text not null,
|
||||
claims_username text not null,
|
||||
claims_email text not null,
|
||||
claims_email_verified boolean not null,
|
||||
claims_groups bytea not null, -- JSON array of strings
|
||||
|
||||
|
||||
connector_id text not null,
|
||||
connector_data bytea,
|
||||
|
||||
|
||||
expiry timestamptz not null
|
||||
);`,
|
||||
`
|
||||
|
@ -129,13 +129,13 @@ var migrations = []migration{
|
|||
client_id text not null,
|
||||
scopes bytea not null, -- JSON array of strings
|
||||
nonce text not null,
|
||||
|
||||
|
||||
claims_user_id text not null,
|
||||
claims_username text not null,
|
||||
claims_email text not null,
|
||||
claims_email_verified boolean not null,
|
||||
claims_groups bytea not null, -- JSON array of strings
|
||||
|
||||
|
||||
connector_id text not null,
|
||||
connector_data bytea
|
||||
);`,
|
||||
|
@ -190,4 +190,23 @@ var migrations = []migration{
|
|||
);`,
|
||||
},
|
||||
},
|
||||
{
|
||||
stmts: []string{`
|
||||
alter table auth_code
|
||||
add column claims_preferred_username text not null default '';`,
|
||||
`
|
||||
alter table auth_request
|
||||
add column claims_preferred_username text not null default '';`,
|
||||
`
|
||||
alter table refresh_token
|
||||
add column claims_preferred_username text not null default '';`,
|
||||
},
|
||||
},
|
||||
{
|
||||
stmts: []string{`
|
||||
alter table offline_session
|
||||
add column connector_data bytea;
|
||||
`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ package sql
|
|||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -12,12 +13,24 @@ func TestPostgresTunables(t *testing.T) {
|
|||
if host == "" {
|
||||
t.Skipf("test environment variable %q not set, skipping", testPostgresEnv)
|
||||
}
|
||||
|
||||
port := uint64(5432)
|
||||
if rawPort := os.Getenv("DEX_POSTGRES_PORT"); rawPort != "" {
|
||||
var err error
|
||||
|
||||
port, err = strconv.ParseUint(rawPort, 10, 32)
|
||||
if err != nil {
|
||||
t.Fatalf("invalid postgres port %q: %s", rawPort, err)
|
||||
}
|
||||
}
|
||||
|
||||
baseCfg := &Postgres{
|
||||
NetworkDB: NetworkDB{
|
||||
Database: getenv("DEX_POSTGRES_DATABASE", "postgres"),
|
||||
User: getenv("DEX_POSTGRES_USER", "postgres"),
|
||||
Password: getenv("DEX_POSTGRES_PASSWORD", "postgres"),
|
||||
Host: host,
|
||||
Port: uint16(port),
|
||||
},
|
||||
SSL: SSL{
|
||||
Mode: pgSSLDisable, // Postgres container doesn't support SSL.
|
||||
|
|
|
@ -137,10 +137,11 @@ type Client struct {
|
|||
|
||||
// Claims represents the ID Token claims supported by the server.
|
||||
type Claims struct {
|
||||
UserID string
|
||||
Username string
|
||||
Email string
|
||||
EmailVerified bool
|
||||
UserID string
|
||||
Username string
|
||||
PreferredUsername string
|
||||
Email string
|
||||
EmailVerified bool
|
||||
|
||||
Groups []string
|
||||
}
|
||||
|
@ -272,6 +273,9 @@ type OfflineSessions struct {
|
|||
// Refresh is a hash table of refresh token reference objects
|
||||
// indexed by the ClientID of the refresh token.
|
||||
Refresh map[string]*RefreshTokenRef
|
||||
|
||||
// Authentication data provided by an upstream source.
|
||||
ConnectorData []byte
|
||||
}
|
||||
|
||||
// Password is an email to password mapping managed by the storage.
|
||||
|
|
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
202
vendor/cloud.google.com/go/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
513
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
513
vendor/cloud.google.com/go/compute/metadata/metadata.go
generated
vendored
Normal file
|
@ -0,0 +1,513 @@
|
|||
// Copyright 2014 Google LLC
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package metadata provides access to Google Compute Engine (GCE)
|
||||
// metadata and API service accounts.
|
||||
//
|
||||
// This package is a wrapper around the GCE metadata service,
|
||||
// as documented at https://developers.google.com/compute/docs/metadata.
|
||||
package metadata // import "cloud.google.com/go/compute/metadata"
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// metadataIP is the documented metadata server IP address.
|
||||
metadataIP = "169.254.169.254"
|
||||
|
||||
// metadataHostEnv is the environment variable specifying the
|
||||
// GCE metadata hostname. If empty, the default value of
|
||||
// metadataIP ("169.254.169.254") is used instead.
|
||||
// This is variable name is not defined by any spec, as far as
|
||||
// I know; it was made up for the Go package.
|
||||
metadataHostEnv = "GCE_METADATA_HOST"
|
||||
|
||||
userAgent = "gcloud-golang/0.1"
|
||||
)
|
||||
|
||||
type cachedValue struct {
|
||||
k string
|
||||
trim bool
|
||||
mu sync.Mutex
|
||||
v string
|
||||
}
|
||||
|
||||
var (
|
||||
projID = &cachedValue{k: "project/project-id", trim: true}
|
||||
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
|
||||
instID = &cachedValue{k: "instance/id", trim: true}
|
||||
)
|
||||
|
||||
var (
|
||||
defaultClient = &Client{hc: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
ResponseHeaderTimeout: 2 * time.Second,
|
||||
},
|
||||
}}
|
||||
subscribeClient = &Client{hc: &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Dial: (&net.Dialer{
|
||||
Timeout: 2 * time.Second,
|
||||
KeepAlive: 30 * time.Second,
|
||||
}).Dial,
|
||||
},
|
||||
}}
|
||||
)
|
||||
|
||||
// NotDefinedError is returned when requested metadata is not defined.
|
||||
//
|
||||
// The underlying string is the suffix after "/computeMetadata/v1/".
|
||||
//
|
||||
// This error is not returned if the value is defined to be the empty
|
||||
// string.
|
||||
type NotDefinedError string
|
||||
|
||||
func (suffix NotDefinedError) Error() string {
|
||||
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
|
||||
}
|
||||
|
||||
func (c *cachedValue) get(cl *Client) (v string, err error) {
|
||||
defer c.mu.Unlock()
|
||||
c.mu.Lock()
|
||||
if c.v != "" {
|
||||
return c.v, nil
|
||||
}
|
||||
if c.trim {
|
||||
v, err = cl.getTrimmed(c.k)
|
||||
} else {
|
||||
v, err = cl.Get(c.k)
|
||||
}
|
||||
if err == nil {
|
||||
c.v = v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
onGCEOnce sync.Once
|
||||
onGCE bool
|
||||
)
|
||||
|
||||
// OnGCE reports whether this process is running on Google Compute Engine.
|
||||
func OnGCE() bool {
|
||||
onGCEOnce.Do(initOnGCE)
|
||||
return onGCE
|
||||
}
|
||||
|
||||
func initOnGCE() {
|
||||
onGCE = testOnGCE()
|
||||
}
|
||||
|
||||
func testOnGCE() bool {
|
||||
// The user explicitly said they're on GCE, so trust them.
|
||||
if os.Getenv(metadataHostEnv) != "" {
|
||||
return true
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
resc := make(chan bool, 2)
|
||||
|
||||
// Try two strategies in parallel.
|
||||
// See https://github.com/googleapis/google-cloud-go/issues/194
|
||||
go func() {
|
||||
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
res, err := defaultClient.hc.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
resc <- false
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
resc <- res.Header.Get("Metadata-Flavor") == "Google"
|
||||
}()
|
||||
|
||||
go func() {
|
||||
addrs, err := net.LookupHost("metadata.google.internal")
|
||||
if err != nil || len(addrs) == 0 {
|
||||
resc <- false
|
||||
return
|
||||
}
|
||||
resc <- strsContains(addrs, metadataIP)
|
||||
}()
|
||||
|
||||
tryHarder := systemInfoSuggestsGCE()
|
||||
if tryHarder {
|
||||
res := <-resc
|
||||
if res {
|
||||
// The first strategy succeeded, so let's use it.
|
||||
return true
|
||||
}
|
||||
// Wait for either the DNS or metadata server probe to
|
||||
// contradict the other one and say we are running on
|
||||
// GCE. Give it a lot of time to do so, since the system
|
||||
// info already suggests we're running on a GCE BIOS.
|
||||
timer := time.NewTimer(5 * time.Second)
|
||||
defer timer.Stop()
|
||||
select {
|
||||
case res = <-resc:
|
||||
return res
|
||||
case <-timer.C:
|
||||
// Too slow. Who knows what this system is.
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// There's no hint from the system info that we're running on
|
||||
// GCE, so use the first probe's result as truth, whether it's
|
||||
// true or false. The goal here is to optimize for speed for
|
||||
// users who are NOT running on GCE. We can't assume that
|
||||
// either a DNS lookup or an HTTP request to a blackholed IP
|
||||
// address is fast. Worst case this should return when the
|
||||
// metaClient's Transport.ResponseHeaderTimeout or
|
||||
// Transport.Dial.Timeout fires (in two seconds).
|
||||
return <-resc
|
||||
}
|
||||
|
||||
// systemInfoSuggestsGCE reports whether the local system (without
|
||||
// doing network requests) suggests that we're running on GCE. If this
|
||||
// returns true, testOnGCE tries a bit harder to reach its metadata
|
||||
// server.
|
||||
func systemInfoSuggestsGCE() bool {
|
||||
if runtime.GOOS != "linux" {
|
||||
// We don't have any non-Linux clues available, at least yet.
|
||||
return false
|
||||
}
|
||||
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
|
||||
name := strings.TrimSpace(string(slurp))
|
||||
return name == "Google" || name == "Google Compute Engine"
|
||||
}
|
||||
|
||||
// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
|
||||
// ResponseHeaderTimeout).
|
||||
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||
return subscribeClient.Subscribe(suffix, fn)
|
||||
}
|
||||
|
||||
// Get calls Client.Get on the default client.
|
||||
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }
|
||||
|
||||
// ProjectID returns the current instance's project ID string.
|
||||
func ProjectID() (string, error) { return defaultClient.ProjectID() }
|
||||
|
||||
// NumericProjectID returns the current instance's numeric project ID.
|
||||
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }
|
||||
|
||||
// InternalIP returns the instance's primary internal IP address.
|
||||
func InternalIP() (string, error) { return defaultClient.InternalIP() }
|
||||
|
||||
// ExternalIP returns the instance's primary external (public) IP address.
|
||||
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }
|
||||
|
||||
// Hostname returns the instance's hostname. This will be of the form
|
||||
// "<instanceID>.c.<projID>.internal".
|
||||
func Hostname() (string, error) { return defaultClient.Hostname() }
|
||||
|
||||
// InstanceTags returns the list of user-defined instance tags,
|
||||
// assigned when initially creating a GCE instance.
|
||||
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }
|
||||
|
||||
// InstanceID returns the current VM's numeric instance ID.
|
||||
func InstanceID() (string, error) { return defaultClient.InstanceID() }
|
||||
|
||||
// InstanceName returns the current VM's instance ID string.
|
||||
func InstanceName() (string, error) { return defaultClient.InstanceName() }
|
||||
|
||||
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||
func Zone() (string, error) { return defaultClient.Zone() }
|
||||
|
||||
// InstanceAttributes calls Client.InstanceAttributes on the default client.
|
||||
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }
|
||||
|
||||
// ProjectAttributes calls Client.ProjectAttributes on the default client.
|
||||
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }
|
||||
|
||||
// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
|
||||
func InstanceAttributeValue(attr string) (string, error) {
|
||||
return defaultClient.InstanceAttributeValue(attr)
|
||||
}
|
||||
|
||||
// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
|
||||
func ProjectAttributeValue(attr string) (string, error) {
|
||||
return defaultClient.ProjectAttributeValue(attr)
|
||||
}
|
||||
|
||||
// Scopes calls Client.Scopes on the default client.
|
||||
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }
|
||||
|
||||
func strsContains(ss []string, s string) bool {
|
||||
for _, v := range ss {
|
||||
if v == s {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// A Client provides metadata.
|
||||
type Client struct {
|
||||
hc *http.Client
|
||||
}
|
||||
|
||||
// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
|
||||
// will use the given http.Client instead of the default client.
|
||||
func NewClient(c *http.Client) *Client {
|
||||
return &Client{hc: c}
|
||||
}
|
||||
|
||||
// getETag returns a value from the metadata service as well as the associated ETag.
|
||||
// This func is otherwise equivalent to Get.
|
||||
func (c *Client) getETag(suffix string) (value, etag string, err error) {
|
||||
// Using a fixed IP makes it very difficult to spoof the metadata service in
|
||||
// a container, which is an important use-case for local testing of cloud
|
||||
// deployments. To enable spoofing of the metadata service, the environment
|
||||
// variable GCE_METADATA_HOST is first inspected to decide where metadata
|
||||
// requests shall go.
|
||||
host := os.Getenv(metadataHostEnv)
|
||||
if host == "" {
|
||||
// Using 169.254.169.254 instead of "metadata" here because Go
|
||||
// binaries built with the "netgo" tag and without cgo won't
|
||||
// know the search suffix for "metadata" is
|
||||
// ".google.internal", and this IP address is documented as
|
||||
// being stable anyway.
|
||||
host = metadataIP
|
||||
}
|
||||
u := "http://" + host + "/computeMetadata/v1/" + suffix
|
||||
req, _ := http.NewRequest("GET", u, nil)
|
||||
req.Header.Set("Metadata-Flavor", "Google")
|
||||
req.Header.Set("User-Agent", userAgent)
|
||||
res, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode == http.StatusNotFound {
|
||||
return "", "", NotDefinedError(suffix)
|
||||
}
|
||||
all, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
|
||||
}
|
||||
return string(all), res.Header.Get("Etag"), nil
|
||||
}
|
||||
|
||||
// Get returns a value from the metadata service.
|
||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||
//
|
||||
// If the GCE_METADATA_HOST environment variable is not defined, a default of
|
||||
// 169.254.169.254 will be used instead.
|
||||
//
|
||||
// If the requested metadata is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
func (c *Client) Get(suffix string) (string, error) {
|
||||
val, _, err := c.getETag(suffix)
|
||||
return val, err
|
||||
}
|
||||
|
||||
func (c *Client) getTrimmed(suffix string) (s string, err error) {
|
||||
s, err = c.Get(suffix)
|
||||
s = strings.TrimSpace(s)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Client) lines(suffix string) ([]string, error) {
|
||||
j, err := c.Get(suffix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s := strings.Split(strings.TrimSpace(j), "\n")
|
||||
for i := range s {
|
||||
s[i] = strings.TrimSpace(s[i])
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// ProjectID returns the current instance's project ID string.
|
||||
func (c *Client) ProjectID() (string, error) { return projID.get(c) }
|
||||
|
||||
// NumericProjectID returns the current instance's numeric project ID.
|
||||
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }
|
||||
|
||||
// InstanceID returns the current VM's numeric instance ID.
|
||||
func (c *Client) InstanceID() (string, error) { return instID.get(c) }
|
||||
|
||||
// InternalIP returns the instance's primary internal IP address.
|
||||
func (c *Client) InternalIP() (string, error) {
|
||||
return c.getTrimmed("instance/network-interfaces/0/ip")
|
||||
}
|
||||
|
||||
// ExternalIP returns the instance's primary external (public) IP address.
|
||||
func (c *Client) ExternalIP() (string, error) {
|
||||
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
|
||||
}
|
||||
|
||||
// Hostname returns the instance's hostname. This will be of the form
|
||||
// "<instanceID>.c.<projID>.internal".
|
||||
func (c *Client) Hostname() (string, error) {
|
||||
return c.getTrimmed("instance/hostname")
|
||||
}
|
||||
|
||||
// InstanceTags returns the list of user-defined instance tags,
|
||||
// assigned when initially creating a GCE instance.
|
||||
func (c *Client) InstanceTags() ([]string, error) {
|
||||
var s []string
|
||||
j, err := c.Get("instance/tags")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// InstanceName returns the current VM's instance ID string.
|
||||
func (c *Client) InstanceName() (string, error) {
|
||||
host, err := c.Hostname()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.Split(host, ".")[0], nil
|
||||
}
|
||||
|
||||
// Zone returns the current VM's zone, such as "us-central1-b".
|
||||
func (c *Client) Zone() (string, error) {
|
||||
zone, err := c.getTrimmed("instance/zone")
|
||||
// zone is of the form "projects/<projNum>/zones/<zoneName>".
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return zone[strings.LastIndex(zone, "/")+1:], nil
|
||||
}
|
||||
|
||||
// InstanceAttributes returns the list of user-defined attributes,
|
||||
// assigned when initially creating a GCE VM instance. The value of an
|
||||
// attribute can be obtained with InstanceAttributeValue.
|
||||
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }
|
||||
|
||||
// ProjectAttributes returns the list of user-defined attributes
|
||||
// applying to the project as a whole, not just this VM. The value of
|
||||
// an attribute can be obtained with ProjectAttributeValue.
|
||||
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }
|
||||
|
||||
// InstanceAttributeValue returns the value of the provided VM
|
||||
// instance attribute.
|
||||
//
|
||||
// If the requested attribute is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
//
|
||||
// InstanceAttributeValue may return ("", nil) if the attribute was
|
||||
// defined to be the empty string.
|
||||
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
|
||||
return c.Get("instance/attributes/" + attr)
|
||||
}
|
||||
|
||||
// ProjectAttributeValue returns the value of the provided
|
||||
// project attribute.
|
||||
//
|
||||
// If the requested attribute is not defined, the returned error will
|
||||
// be of type NotDefinedError.
|
||||
//
|
||||
// ProjectAttributeValue may return ("", nil) if the attribute was
|
||||
// defined to be the empty string.
|
||||
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
|
||||
return c.Get("project/attributes/" + attr)
|
||||
}
|
||||
|
||||
// Scopes returns the service account scopes for the given account.
|
||||
// The account may be empty or the string "default" to use the instance's
|
||||
// main account.
|
||||
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
|
||||
if serviceAccount == "" {
|
||||
serviceAccount = "default"
|
||||
}
|
||||
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
|
||||
}
|
||||
|
||||
// Subscribe subscribes to a value from the metadata service.
|
||||
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
|
||||
// The suffix may contain query parameters.
|
||||
//
|
||||
// Subscribe calls fn with the latest metadata value indicated by the provided
|
||||
// suffix. If the metadata value is deleted, fn is called with the empty string
|
||||
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
|
||||
// is deleted. Subscribe returns the error value returned from the last call to
|
||||
// fn, which may be nil when ok == false.
|
||||
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
|
||||
const failedSubscribeSleep = time.Second * 5
|
||||
|
||||
// First check to see if the metadata value exists at all.
|
||||
val, lastETag, err := c.getETag(suffix)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := fn(val, true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ok := true
|
||||
if strings.ContainsRune(suffix, '?') {
|
||||
suffix += "&wait_for_change=true&last_etag="
|
||||
} else {
|
||||
suffix += "?wait_for_change=true&last_etag="
|
||||
}
|
||||
for {
|
||||
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
|
||||
if err != nil {
|
||||
if _, deleted := err.(NotDefinedError); !deleted {
|
||||
time.Sleep(failedSubscribeSleep)
|
||||
continue // Retry on other errors.
|
||||
}
|
||||
ok = false
|
||||
}
|
||||
lastETag = etag
|
||||
|
||||
if err := fn(val, ok); err != nil || !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Error contains an error response from the server.
|
||||
type Error struct {
|
||||
// Code is the HTTP response status code.
|
||||
Code int
|
||||
// Message is the server response message.
|
||||
Message string
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
|
||||
}
|
27
vendor/github.com/googleapis/gax-go/v2/LICENSE
generated
vendored
Normal file
27
vendor/github.com/googleapis/gax-go/v2/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright 2016, Google Inc.
|
||||
All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
161
vendor/github.com/googleapis/gax-go/v2/call_option.go
generated
vendored
Normal file
161
vendor/github.com/googleapis/gax-go/v2/call_option.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// CallOption is an option used by Invoke to control behaviors of RPC calls.
|
||||
// CallOption works by modifying relevant fields of CallSettings.
|
||||
type CallOption interface {
|
||||
// Resolve applies the option by modifying cs.
|
||||
Resolve(cs *CallSettings)
|
||||
}
|
||||
|
||||
// Retryer is used by Invoke to determine retry behavior.
|
||||
type Retryer interface {
|
||||
// Retry reports whether a request should be retriedand how long to pause before retrying
|
||||
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
|
||||
Retry(err error) (pause time.Duration, shouldRetry bool)
|
||||
}
|
||||
|
||||
type retryerOption func() Retryer
|
||||
|
||||
func (o retryerOption) Resolve(s *CallSettings) {
|
||||
s.Retry = o
|
||||
}
|
||||
|
||||
// WithRetry sets CallSettings.Retry to fn.
|
||||
func WithRetry(fn func() Retryer) CallOption {
|
||||
return retryerOption(fn)
|
||||
}
|
||||
|
||||
// OnCodes returns a Retryer that retries if and only if
|
||||
// the previous attempt returns a GRPC error whose error code is stored in cc.
|
||||
// Pause times between retries are specified by bo.
|
||||
//
|
||||
// bo is only used for its parameters; each Retryer has its own copy.
|
||||
func OnCodes(cc []codes.Code, bo Backoff) Retryer {
|
||||
return &boRetryer{
|
||||
backoff: bo,
|
||||
codes: append([]codes.Code(nil), cc...),
|
||||
}
|
||||
}
|
||||
|
||||
type boRetryer struct {
|
||||
backoff Backoff
|
||||
codes []codes.Code
|
||||
}
|
||||
|
||||
func (r *boRetryer) Retry(err error) (time.Duration, bool) {
|
||||
st, ok := status.FromError(err)
|
||||
if !ok {
|
||||
return 0, false
|
||||
}
|
||||
c := st.Code()
|
||||
for _, rc := range r.codes {
|
||||
if c == rc {
|
||||
return r.backoff.Pause(), true
|
||||
}
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
// Backoff implements exponential backoff.
|
||||
// The wait time between retries is a random value between 0 and the "retry envelope".
|
||||
// The envelope starts at Initial and increases by the factor of Multiplier every retry,
|
||||
// but is capped at Max.
|
||||
type Backoff struct {
|
||||
// Initial is the initial value of the retry envelope, defaults to 1 second.
|
||||
Initial time.Duration
|
||||
|
||||
// Max is the maximum value of the retry envelope, defaults to 30 seconds.
|
||||
Max time.Duration
|
||||
|
||||
// Multiplier is the factor by which the retry envelope increases.
|
||||
// It should be greater than 1 and defaults to 2.
|
||||
Multiplier float64
|
||||
|
||||
// cur is the current retry envelope
|
||||
cur time.Duration
|
||||
}
|
||||
|
||||
// Pause returns the next time.Duration that the caller should use to backoff.
|
||||
func (bo *Backoff) Pause() time.Duration {
|
||||
if bo.Initial == 0 {
|
||||
bo.Initial = time.Second
|
||||
}
|
||||
if bo.cur == 0 {
|
||||
bo.cur = bo.Initial
|
||||
}
|
||||
if bo.Max == 0 {
|
||||
bo.Max = 30 * time.Second
|
||||
}
|
||||
if bo.Multiplier < 1 {
|
||||
bo.Multiplier = 2
|
||||
}
|
||||
// Select a duration between 1ns and the current max. It might seem
|
||||
// counterintuitive to have so much jitter, but
|
||||
// https://www.awsarchitectureblog.com/2015/03/backoff.html argues that
|
||||
// that is the best strategy.
|
||||
d := time.Duration(1 + rand.Int63n(int64(bo.cur)))
|
||||
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier)
|
||||
if bo.cur > bo.Max {
|
||||
bo.cur = bo.Max
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
type grpcOpt []grpc.CallOption
|
||||
|
||||
func (o grpcOpt) Resolve(s *CallSettings) {
|
||||
s.GRPC = o
|
||||
}
|
||||
|
||||
// WithGRPCOptions allows passing gRPC call options during client creation.
|
||||
func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
|
||||
return grpcOpt(append([]grpc.CallOption(nil), opt...))
|
||||
}
|
||||
|
||||
// CallSettings allow fine-grained control over how calls are made.
|
||||
type CallSettings struct {
|
||||
// Retry returns a Retryer to be used to control retry logic of a method call.
|
||||
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
|
||||
Retry func() Retryer
|
||||
|
||||
// CallOptions to be forwarded to GRPC.
|
||||
GRPC []grpc.CallOption
|
||||
}
|
39
vendor/github.com/googleapis/gax-go/v2/gax.go
generated
vendored
Normal file
39
vendor/github.com/googleapis/gax-go/v2/gax.go
generated
vendored
Normal file
|
@ -0,0 +1,39 @@
|
|||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
// Package gax contains a set of modules which aid the development of APIs
|
||||
// for clients and servers based on gRPC and Google API conventions.
|
||||
//
|
||||
// Application code will rarely need to use this library directly.
|
||||
// However, code generated automatically from API definition files can use it
|
||||
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
|
||||
package gax
|
||||
|
||||
// Version specifies the gax-go version being used.
|
||||
const Version = "2.0.4"
|
3
vendor/github.com/googleapis/gax-go/v2/go.mod
generated
vendored
Normal file
3
vendor/github.com/googleapis/gax-go/v2/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
module github.com/googleapis/gax-go/v2
|
||||
|
||||
require google.golang.org/grpc v1.19.0
|
25
vendor/github.com/googleapis/gax-go/v2/go.sum
generated
vendored
Normal file
25
vendor/github.com/googleapis/gax-go/v2/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
53
vendor/github.com/googleapis/gax-go/v2/header.go
generated
vendored
Normal file
53
vendor/github.com/googleapis/gax-go/v2/header.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2018, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import "bytes"
|
||||
|
||||
// XGoogHeader is for use by the Google Cloud Libraries only.
|
||||
//
|
||||
// XGoogHeader formats key-value pairs.
|
||||
// The resulting string is suitable for x-goog-api-client header.
|
||||
func XGoogHeader(keyval ...string) string {
|
||||
if len(keyval) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(keyval)%2 != 0 {
|
||||
panic("gax.Header: odd argument count")
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
for i := 0; i < len(keyval); i += 2 {
|
||||
buf.WriteByte(' ')
|
||||
buf.WriteString(keyval[i])
|
||||
buf.WriteByte('/')
|
||||
buf.WriteString(keyval[i+1])
|
||||
}
|
||||
return buf.String()[1:]
|
||||
}
|
99
vendor/github.com/googleapis/gax-go/v2/invoke.go
generated
vendored
Normal file
99
vendor/github.com/googleapis/gax-go/v2/invoke.go
generated
vendored
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2016, Google Inc.
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following disclaimer
|
||||
// in the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
package gax
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// APICall is a user defined call stub.
|
||||
type APICall func(context.Context, CallSettings) error
|
||||
|
||||
// Invoke calls the given APICall,
|
||||
// performing retries as specified by opts, if any.
|
||||
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
|
||||
var settings CallSettings
|
||||
for _, opt := range opts {
|
||||
opt.Resolve(&settings)
|
||||
}
|
||||
return invoke(ctx, call, settings, Sleep)
|
||||
}
|
||||
|
||||
// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
|
||||
// If interrupted, Sleep returns ctx.Err().
|
||||
func Sleep(ctx context.Context, d time.Duration) error {
|
||||
t := time.NewTimer(d)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
t.Stop()
|
||||
return ctx.Err()
|
||||
case <-t.C:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
type sleeper func(ctx context.Context, d time.Duration) error
|
||||
|
||||
// invoke implements Invoke, taking an additional sleeper argument for testing.
|
||||
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
|
||||
var retryer Retryer
|
||||
for {
|
||||
err := call(ctx, settings)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if settings.Retry == nil {
|
||||
return err
|
||||
}
|
||||
// Never retry permanent certificate errors. (e.x. if ca-certificates
|
||||
// are not installed). We should only make very few, targeted
|
||||
// exceptions: many (other) status=Unavailable should be retried, such
|
||||
// as if there's a network hiccup, or the internet goes out for a
|
||||
// minute. This is also why here we are doing string parsing instead of
|
||||
// simply making Unavailable a non-retried code elsewhere.
|
||||
if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
|
||||
return err
|
||||
}
|
||||
if retryer == nil {
|
||||
if r := settings.Retry(); r != nil {
|
||||
retryer = r
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if d, ok := retryer.Retry(err); !ok {
|
||||
return err
|
||||
} else if err = sp(ctx, d); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
362
vendor/github.com/hashicorp/golang-lru/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,362 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
161
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
161
vendor/github.com/hashicorp/golang-lru/simplelru/lru.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
package simplelru
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// EvictCallback is used to get a callback when a cache entry is evicted
|
||||
type EvictCallback func(key interface{}, value interface{})
|
||||
|
||||
// LRU implements a non-thread safe fixed size LRU cache
|
||||
type LRU struct {
|
||||
size int
|
||||
evictList *list.List
|
||||
items map[interface{}]*list.Element
|
||||
onEvict EvictCallback
|
||||
}
|
||||
|
||||
// entry is used to hold a value in the evictList
|
||||
type entry struct {
|
||||
key interface{}
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewLRU constructs an LRU of the given size
|
||||
func NewLRU(size int, onEvict EvictCallback) (*LRU, error) {
|
||||
if size <= 0 {
|
||||
return nil, errors.New("Must provide a positive size")
|
||||
}
|
||||
c := &LRU{
|
||||
size: size,
|
||||
evictList: list.New(),
|
||||
items: make(map[interface{}]*list.Element),
|
||||
onEvict: onEvict,
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// Purge is used to completely clear the cache.
|
||||
func (c *LRU) Purge() {
|
||||
for k, v := range c.items {
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(k, v.Value.(*entry).value)
|
||||
}
|
||||
delete(c.items, k)
|
||||
}
|
||||
c.evictList.Init()
|
||||
}
|
||||
|
||||
// Add adds a value to the cache. Returns true if an eviction occurred.
|
||||
func (c *LRU) Add(key, value interface{}) (evicted bool) {
|
||||
// Check for existing item
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
ent.Value.(*entry).value = value
|
||||
return false
|
||||
}
|
||||
|
||||
// Add new item
|
||||
ent := &entry{key, value}
|
||||
entry := c.evictList.PushFront(ent)
|
||||
c.items[key] = entry
|
||||
|
||||
evict := c.evictList.Len() > c.size
|
||||
// Verify size not exceeded
|
||||
if evict {
|
||||
c.removeOldest()
|
||||
}
|
||||
return evict
|
||||
}
|
||||
|
||||
// Get looks up a key's value from the cache.
|
||||
func (c *LRU) Get(key interface{}) (value interface{}, ok bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.evictList.MoveToFront(ent)
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Contains checks if a key is in the cache, without updating the recent-ness
|
||||
// or deleting it for being stale.
|
||||
func (c *LRU) Contains(key interface{}) (ok bool) {
|
||||
_, ok = c.items[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Peek returns the key value (or undefined if not found) without updating
|
||||
// the "recently used"-ness of the key.
|
||||
func (c *LRU) Peek(key interface{}) (value interface{}, ok bool) {
|
||||
var ent *list.Element
|
||||
if ent, ok = c.items[key]; ok {
|
||||
return ent.Value.(*entry).value, true
|
||||
}
|
||||
return nil, ok
|
||||
}
|
||||
|
||||
// Remove removes the provided key from the cache, returning if the
|
||||
// key was contained.
|
||||
func (c *LRU) Remove(key interface{}) (present bool) {
|
||||
if ent, ok := c.items[key]; ok {
|
||||
c.removeElement(ent)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveOldest removes the oldest item from the cache.
|
||||
func (c *LRU) RemoveOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
kv := ent.Value.(*entry)
|
||||
return kv.key, kv.value, true
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// GetOldest returns the oldest entry
|
||||
func (c *LRU) GetOldest() (key interface{}, value interface{}, ok bool) {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
kv := ent.Value.(*entry)
|
||||
return kv.key, kv.value, true
|
||||
}
|
||||
return nil, nil, false
|
||||
}
|
||||
|
||||
// Keys returns a slice of the keys in the cache, from oldest to newest.
|
||||
func (c *LRU) Keys() []interface{} {
|
||||
keys := make([]interface{}, len(c.items))
|
||||
i := 0
|
||||
for ent := c.evictList.Back(); ent != nil; ent = ent.Prev() {
|
||||
keys[i] = ent.Value.(*entry).key
|
||||
i++
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
// Len returns the number of items in the cache.
|
||||
func (c *LRU) Len() int {
|
||||
return c.evictList.Len()
|
||||
}
|
||||
|
||||
// removeOldest removes the oldest item from the cache.
|
||||
func (c *LRU) removeOldest() {
|
||||
ent := c.evictList.Back()
|
||||
if ent != nil {
|
||||
c.removeElement(ent)
|
||||
}
|
||||
}
|
||||
|
||||
// removeElement is used to remove a given list element from the cache
|
||||
func (c *LRU) removeElement(e *list.Element) {
|
||||
c.evictList.Remove(e)
|
||||
kv := e.Value.(*entry)
|
||||
delete(c.items, kv.key)
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(kv.key, kv.value)
|
||||
}
|
||||
}
|
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
36
vendor/github.com/hashicorp/golang-lru/simplelru/lru_interface.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
package simplelru
|
||||
|
||||
// LRUCache is the interface for simple LRU cache.
|
||||
type LRUCache interface {
|
||||
// Adds a value to the cache, returns true if an eviction occurred and
|
||||
// updates the "recently used"-ness of the key.
|
||||
Add(key, value interface{}) bool
|
||||
|
||||
// Returns key's value from the cache and
|
||||
// updates the "recently used"-ness of the key. #value, isFound
|
||||
Get(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Check if a key exsists in cache without updating the recent-ness.
|
||||
Contains(key interface{}) (ok bool)
|
||||
|
||||
// Returns key's value without updating the "recently used"-ness of the key.
|
||||
Peek(key interface{}) (value interface{}, ok bool)
|
||||
|
||||
// Removes a key from the cache.
|
||||
Remove(key interface{}) bool
|
||||
|
||||
// Removes the oldest entry from cache.
|
||||
RemoveOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns the oldest entry from the cache. #key, value, isFound
|
||||
GetOldest() (interface{}, interface{}, bool)
|
||||
|
||||
// Returns a slice of the keys in the cache, from oldest to newest.
|
||||
Keys() []interface{}
|
||||
|
||||
// Returns the number of items in the cache.
|
||||
Len() int
|
||||
|
||||
// Clear all cache entries
|
||||
Purge()
|
||||
}
|
93
vendor/github.com/lib/pq/oid/gen.go
generated
vendored
93
vendor/github.com/lib/pq/oid/gen.go
generated
vendored
|
@ -1,93 +0,0 @@
|
|||
// +build ignore
|
||||
|
||||
// Generate the table of OID values
|
||||
// Run with 'go run gen.go'.
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
)
|
||||
|
||||
// OID represent a postgres Object Identifier Type.
|
||||
type OID struct {
|
||||
ID int
|
||||
Type string
|
||||
}
|
||||
|
||||
// Name returns an upper case version of the oid type.
|
||||
func (o OID) Name() string {
|
||||
return strings.ToUpper(o.Type)
|
||||
}
|
||||
|
||||
func main() {
|
||||
datname := os.Getenv("PGDATABASE")
|
||||
sslmode := os.Getenv("PGSSLMODE")
|
||||
|
||||
if datname == "" {
|
||||
os.Setenv("PGDATABASE", "pqgotest")
|
||||
}
|
||||
|
||||
if sslmode == "" {
|
||||
os.Setenv("PGSSLMODE", "disable")
|
||||
}
|
||||
|
||||
db, err := sql.Open("postgres", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rows, err := db.Query(`
|
||||
SELECT typname, oid
|
||||
FROM pg_type WHERE oid < 10000
|
||||
ORDER BY oid;
|
||||
`)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
oids := make([]*OID, 0)
|
||||
for rows.Next() {
|
||||
var oid OID
|
||||
if err = rows.Scan(&oid.Type, &oid.ID); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
oids = append(oids, &oid)
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmd := exec.Command("gofmt")
|
||||
cmd.Stderr = os.Stderr
|
||||
w, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
f, err := os.Create("types.go")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
cmd.Stdout = f
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Fprintln(w, "// Code generated by gen.go. DO NOT EDIT.")
|
||||
fmt.Fprintln(w, "\npackage oid")
|
||||
fmt.Fprintln(w, "const (")
|
||||
for _, oid := range oids {
|
||||
fmt.Fprintf(w, "T_%s Oid = %d\n", oid.Type, oid.ID)
|
||||
}
|
||||
fmt.Fprintln(w, ")")
|
||||
fmt.Fprintln(w, "var TypeName = map[Oid]string{")
|
||||
for _, oid := range oids {
|
||||
fmt.Fprintf(w, "T_%s: \"%s\",\n", oid.Type, oid.Name())
|
||||
}
|
||||
fmt.Fprintln(w, "}")
|
||||
w.Close()
|
||||
cmd.Wait()
|
||||
}
|
9
vendor/go.opencensus.io/.gitignore
generated
vendored
Normal file
9
vendor/go.opencensus.io/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
/.idea/
|
||||
|
||||
# go.opencensus.io/exporter/aws
|
||||
/exporter/aws/
|
||||
|
||||
# Exclude vendor, use dep ensure after checkout:
|
||||
/vendor/github.com/
|
||||
/vendor/golang.org/
|
||||
/vendor/google.golang.org/
|
17
vendor/go.opencensus.io/.travis.yml
generated
vendored
Normal file
17
vendor/go.opencensus.io/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
language: go
|
||||
|
||||
go_import_path: go.opencensus.io
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
|
||||
env:
|
||||
global:
|
||||
GO111MODULE=on
|
||||
|
||||
before_script:
|
||||
- make install-tools
|
||||
|
||||
script:
|
||||
- make travis-ci
|
||||
- go run internal/check/version.go # TODO move this to makefile
|
1
vendor/go.opencensus.io/AUTHORS
generated
vendored
Normal file
1
vendor/go.opencensus.io/AUTHORS
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
Google Inc.
|
63
vendor/go.opencensus.io/CONTRIBUTING.md
generated
vendored
Normal file
63
vendor/go.opencensus.io/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
# How to contribute
|
||||
|
||||
We'd love to accept your patches and contributions to this project. There are
|
||||
just a few small guidelines you need to follow.
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
Contributions to this project must be accompanied by a Contributor License
|
||||
Agreement. You (or your employer) retain the copyright to your contribution,
|
||||
this simply gives us permission to use and redistribute your contributions as
|
||||
part of the project. Head over to <https://cla.developers.google.com/> to see
|
||||
your current agreements on file or to sign a new one.
|
||||
|
||||
You generally only need to submit a CLA once, so if you've already submitted one
|
||||
(even if it was for a different project), you probably don't need to do it
|
||||
again.
|
||||
|
||||
## Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use GitHub pull requests for this purpose. Consult [GitHub Help] for more
|
||||
information on using pull requests.
|
||||
|
||||
[GitHub Help]: https://help.github.com/articles/about-pull-requests/
|
||||
|
||||
## Instructions
|
||||
|
||||
Fork the repo, checkout the upstream repo to your GOPATH by:
|
||||
|
||||
```
|
||||
$ go get -d go.opencensus.io
|
||||
```
|
||||
|
||||
Add your fork as an origin:
|
||||
|
||||
```
|
||||
cd $(go env GOPATH)/src/go.opencensus.io
|
||||
git remote add fork git@github.com:YOUR_GITHUB_USERNAME/opencensus-go.git
|
||||
```
|
||||
|
||||
Run tests:
|
||||
|
||||
```
|
||||
$ make install-tools # Only first time.
|
||||
$ make
|
||||
```
|
||||
|
||||
Checkout a new branch, make modifications and push the branch to your fork:
|
||||
|
||||
```
|
||||
$ git checkout -b feature
|
||||
# edit files
|
||||
$ git commit
|
||||
$ git push fork feature
|
||||
```
|
||||
|
||||
Open a pull request against the main opencensus-go repo.
|
||||
|
||||
## General Notes
|
||||
This project uses Appveyor and Travis for CI.
|
||||
|
||||
The dependencies are managed with `go mod` if you work with the sources under your
|
||||
`$GOPATH` you need to set the environment variable `GO111MODULE=on`.
|
231
vendor/go.opencensus.io/Gopkg.lock
generated
vendored
Normal file
231
vendor/go.opencensus.io/Gopkg.lock
generated
vendored
Normal file
|
@ -0,0 +1,231 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:eee9386329f4fcdf8d6c0def0c9771b634bdd5ba460d888aa98c17d59b37a76c"
|
||||
name = "git.apache.org/thrift.git"
|
||||
packages = ["lib/go/thrift"]
|
||||
pruneopts = "UT"
|
||||
revision = "6e67faa92827ece022380b211c2caaadd6145bf5"
|
||||
source = "github.com/apache/thrift"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:d6afaeed1502aa28e80a4ed0981d570ad91b2579193404256ce672ed0a609e0d"
|
||||
name = "github.com/beorn7/perks"
|
||||
packages = ["quantile"]
|
||||
pruneopts = "UT"
|
||||
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:4c0989ca0bcd10799064318923b9bc2db6b4d6338dd75f3f2d86c3511aaaf5cf"
|
||||
name = "github.com/golang/protobuf"
|
||||
packages = [
|
||||
"proto",
|
||||
"ptypes",
|
||||
"ptypes/any",
|
||||
"ptypes/duration",
|
||||
"ptypes/timestamp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "aa810b61a9c79d51363740d207bb46cf8e620ed5"
|
||||
version = "v1.2.0"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:ff5ebae34cfbf047d505ee150de27e60570e8c394b3b8fdbb720ff6ac71985fc"
|
||||
name = "github.com/matttproud/golang_protobuf_extensions"
|
||||
packages = ["pbutil"]
|
||||
pruneopts = "UT"
|
||||
revision = "c12348ce28de40eed0136aa2b644d0ee0650e56c"
|
||||
version = "v1.0.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:824c8f3aa4c5f23928fa84ebbd5ed2e9443b3f0cb958a40c1f2fbed5cf5e64b1"
|
||||
name = "github.com/openzipkin/zipkin-go"
|
||||
packages = [
|
||||
".",
|
||||
"idgenerator",
|
||||
"model",
|
||||
"propagation",
|
||||
"reporter",
|
||||
"reporter/http",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "d455a5674050831c1e187644faa4046d653433c2"
|
||||
version = "v0.1.1"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:d14a5f4bfecf017cb780bdde1b6483e5deb87e12c332544d2c430eda58734bcb"
|
||||
name = "github.com/prometheus/client_golang"
|
||||
packages = [
|
||||
"prometheus",
|
||||
"prometheus/promhttp",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c5b7fccd204277076155f10851dad72b76a49317"
|
||||
version = "v0.8.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2d5cd61daa5565187e1d96bae64dbbc6080dacf741448e9629c64fd93203b0d4"
|
||||
name = "github.com/prometheus/client_model"
|
||||
packages = ["go"]
|
||||
pruneopts = "UT"
|
||||
revision = "5c3871d89910bfb32f5fcab2aa4b9ec68e65a99f"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:63b68062b8968092eb86bedc4e68894bd096ea6b24920faca8b9dcf451f54bb5"
|
||||
name = "github.com/prometheus/common"
|
||||
packages = [
|
||||
"expfmt",
|
||||
"internal/bitbucket.org/ww/goautoneg",
|
||||
"model",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "c7de2306084e37d54b8be01f3541a8464345e9a5"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:8c49953a1414305f2ff5465147ee576dd705487c35b15918fcd4efdc0cb7a290"
|
||||
name = "github.com/prometheus/procfs"
|
||||
packages = [
|
||||
".",
|
||||
"internal/util",
|
||||
"nfs",
|
||||
"xfs",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "05ee40e3a273f7245e8777337fc7b46e533a9a92"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:deafe4ab271911fec7de5b693d7faae3f38796d9eb8622e2b9e7df42bb3dfea9"
|
||||
name = "golang.org/x/net"
|
||||
packages = [
|
||||
"context",
|
||||
"http/httpguts",
|
||||
"http2",
|
||||
"http2/hpack",
|
||||
"idna",
|
||||
"internal/timeseries",
|
||||
"trace",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "922f4815f713f213882e8ef45e0d315b164d705c"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:e0140c0c868c6e0f01c0380865194592c011fe521d6e12d78bfd33e756fe018a"
|
||||
name = "golang.org/x/sync"
|
||||
packages = ["semaphore"]
|
||||
pruneopts = "UT"
|
||||
revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:a3f00ac457c955fe86a41e1495e8f4c54cb5399d609374c5cc26aa7d72e542c8"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
pruneopts = "UT"
|
||||
revision = "3b58ed4ad3395d483fc92d5d14123ce2c3581fec"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:a2ab62866c75542dd18d2b069fec854577a20211d7c0ea6ae746072a1dccdd18"
|
||||
name = "golang.org/x/text"
|
||||
packages = [
|
||||
"collate",
|
||||
"collate/build",
|
||||
"internal/colltab",
|
||||
"internal/gen",
|
||||
"internal/tag",
|
||||
"internal/triegen",
|
||||
"internal/ucd",
|
||||
"language",
|
||||
"secure/bidirule",
|
||||
"transform",
|
||||
"unicode/bidi",
|
||||
"unicode/cldr",
|
||||
"unicode/norm",
|
||||
"unicode/rangetable",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0"
|
||||
version = "v0.3.0"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:c0c17c94fe8bc1ab34e7f586a4a8b788c5e1f4f9f750ff23395b8b2f5a523530"
|
||||
name = "google.golang.org/api"
|
||||
packages = ["support/bundler"]
|
||||
pruneopts = "UT"
|
||||
revision = "e21acd801f91da814261b938941d193bb036441a"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:077c1c599507b3b3e9156d17d36e1e61928ee9b53a5b420f10f28ebd4a0b275c"
|
||||
name = "google.golang.org/genproto"
|
||||
packages = ["googleapis/rpc/status"]
|
||||
pruneopts = "UT"
|
||||
revision = "c66870c02cf823ceb633bcd05be3c7cda29976f4"
|
||||
|
||||
[[projects]]
|
||||
digest = "1:3dd7996ce6bf52dec6a2f69fa43e7c4cefea1d4dfa3c8ab7a5f8a9f7434e239d"
|
||||
name = "google.golang.org/grpc"
|
||||
packages = [
|
||||
".",
|
||||
"balancer",
|
||||
"balancer/base",
|
||||
"balancer/roundrobin",
|
||||
"codes",
|
||||
"connectivity",
|
||||
"credentials",
|
||||
"encoding",
|
||||
"encoding/proto",
|
||||
"grpclog",
|
||||
"internal",
|
||||
"internal/backoff",
|
||||
"internal/channelz",
|
||||
"internal/envconfig",
|
||||
"internal/grpcrand",
|
||||
"internal/transport",
|
||||
"keepalive",
|
||||
"metadata",
|
||||
"naming",
|
||||
"peer",
|
||||
"resolver",
|
||||
"resolver/dns",
|
||||
"resolver/passthrough",
|
||||
"stats",
|
||||
"status",
|
||||
"tap",
|
||||
]
|
||||
pruneopts = "UT"
|
||||
revision = "32fb0ac620c32ba40a4626ddf94d90d12cce3455"
|
||||
version = "v1.14.0"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
input-imports = [
|
||||
"git.apache.org/thrift.git/lib/go/thrift",
|
||||
"github.com/golang/protobuf/proto",
|
||||
"github.com/openzipkin/zipkin-go",
|
||||
"github.com/openzipkin/zipkin-go/model",
|
||||
"github.com/openzipkin/zipkin-go/reporter",
|
||||
"github.com/openzipkin/zipkin-go/reporter/http",
|
||||
"github.com/prometheus/client_golang/prometheus",
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp",
|
||||
"golang.org/x/net/context",
|
||||
"golang.org/x/net/http2",
|
||||
"google.golang.org/api/support/bundler",
|
||||
"google.golang.org/grpc",
|
||||
"google.golang.org/grpc/codes",
|
||||
"google.golang.org/grpc/grpclog",
|
||||
"google.golang.org/grpc/metadata",
|
||||
"google.golang.org/grpc/stats",
|
||||
"google.golang.org/grpc/status",
|
||||
]
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
36
vendor/go.opencensus.io/Gopkg.toml
generated
vendored
Normal file
36
vendor/go.opencensus.io/Gopkg.toml
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
# For v0.x.y dependencies, prefer adding a constraints of the form: version=">= 0.x.y"
|
||||
# to avoid locking to a particular minor version which can cause dep to not be
|
||||
# able to find a satisfying dependency graph.
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "git.apache.org/thrift.git"
|
||||
source = "github.com/apache/thrift"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/golang/protobuf"
|
||||
version = "1.0.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/openzipkin/zipkin-go"
|
||||
version = ">=0.1.0"
|
||||
|
||||
[[constraint]]
|
||||
name = "github.com/prometheus/client_golang"
|
||||
version = ">=0.8.0"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/net"
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "google.golang.org/api"
|
||||
|
||||
[[constraint]]
|
||||
name = "google.golang.org/grpc"
|
||||
version = "1.11.3"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
202
vendor/go.opencensus.io/LICENSE
generated
vendored
Normal file
202
vendor/go.opencensus.io/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
96
vendor/go.opencensus.io/Makefile
generated
vendored
Normal file
96
vendor/go.opencensus.io/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,96 @@
|
|||
# TODO: Fix this on windows.
|
||||
ALL_SRC := $(shell find . -name '*.go' \
|
||||
-not -path './vendor/*' \
|
||||
-not -path '*/gen-go/*' \
|
||||
-type f | sort)
|
||||
ALL_PKGS := $(shell go list $(sort $(dir $(ALL_SRC))))
|
||||
|
||||
GOTEST_OPT?=-v -race -timeout 30s
|
||||
GOTEST_OPT_WITH_COVERAGE = $(GOTEST_OPT) -coverprofile=coverage.txt -covermode=atomic
|
||||
GOTEST=go test
|
||||
GOFMT=gofmt
|
||||
GOLINT=golint
|
||||
GOVET=go vet
|
||||
EMBEDMD=embedmd
|
||||
# TODO decide if we need to change these names.
|
||||
TRACE_ID_LINT_EXCEPTION="type name will be used as trace.TraceID by other packages"
|
||||
TRACE_OPTION_LINT_EXCEPTION="type name will be used as trace.TraceOptions by other packages"
|
||||
README_FILES := $(shell find . -name '*README.md' | sort | tr '\n' ' ')
|
||||
|
||||
.DEFAULT_GOAL := fmt-lint-vet-embedmd-test
|
||||
|
||||
.PHONY: fmt-lint-vet-embedmd-test
|
||||
fmt-lint-vet-embedmd-test: fmt lint vet embedmd test
|
||||
|
||||
# TODO enable test-with-coverage in tavis
|
||||
.PHONY: travis-ci
|
||||
travis-ci: fmt lint vet embedmd test test-386
|
||||
|
||||
all-pkgs:
|
||||
@echo $(ALL_PKGS) | tr ' ' '\n' | sort
|
||||
|
||||
all-srcs:
|
||||
@echo $(ALL_SRC) | tr ' ' '\n' | sort
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
$(GOTEST) $(GOTEST_OPT) $(ALL_PKGS)
|
||||
|
||||
.PHONY: test-386
|
||||
test-386:
|
||||
GOARCH=386 $(GOTEST) -v -timeout 30s $(ALL_PKGS)
|
||||
|
||||
.PHONY: test-with-coverage
|
||||
test-with-coverage:
|
||||
$(GOTEST) $(GOTEST_OPT_WITH_COVERAGE) $(ALL_PKGS)
|
||||
|
||||
.PHONY: fmt
|
||||
fmt:
|
||||
@FMTOUT=`$(GOFMT) -s -l $(ALL_SRC) 2>&1`; \
|
||||
if [ "$$FMTOUT" ]; then \
|
||||
echo "$(GOFMT) FAILED => gofmt the following files:\n"; \
|
||||
echo "$$FMTOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Fmt finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
@LINTOUT=`$(GOLINT) $(ALL_PKGS) | grep -v $(TRACE_ID_LINT_EXCEPTION) | grep -v $(TRACE_OPTION_LINT_EXCEPTION) 2>&1`; \
|
||||
if [ "$$LINTOUT" ]; then \
|
||||
echo "$(GOLINT) FAILED => clean the following lint errors:\n"; \
|
||||
echo "$$LINTOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Lint finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: vet
|
||||
vet:
|
||||
# TODO: Understand why go vet downloads "github.com/google/go-cmp v0.2.0"
|
||||
@VETOUT=`$(GOVET) ./... | grep -v "go: downloading" 2>&1`; \
|
||||
if [ "$$VETOUT" ]; then \
|
||||
echo "$(GOVET) FAILED => go vet the following files:\n"; \
|
||||
echo "$$VETOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Vet finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: embedmd
|
||||
embedmd:
|
||||
@EMBEDMDOUT=`$(EMBEDMD) -d $(README_FILES) 2>&1`; \
|
||||
if [ "$$EMBEDMDOUT" ]; then \
|
||||
echo "$(EMBEDMD) FAILED => embedmd the following files:\n"; \
|
||||
echo "$$EMBEDMDOUT\n"; \
|
||||
exit 1; \
|
||||
else \
|
||||
echo "Embedmd finished successfully"; \
|
||||
fi
|
||||
|
||||
.PHONY: install-tools
|
||||
install-tools:
|
||||
go get -u golang.org/x/tools/cmd/cover
|
||||
go get -u golang.org/x/lint/golint
|
||||
go get -u github.com/rakyll/embedmd
|
263
vendor/go.opencensus.io/README.md
generated
vendored
Normal file
263
vendor/go.opencensus.io/README.md
generated
vendored
Normal file
|
@ -0,0 +1,263 @@
|
|||
# OpenCensus Libraries for Go
|
||||
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Windows Build Status][appveyor-image]][appveyor-url]
|
||||
[![GoDoc][godoc-image]][godoc-url]
|
||||
[![Gitter chat][gitter-image]][gitter-url]
|
||||
|
||||
OpenCensus Go is a Go implementation of OpenCensus, a toolkit for
|
||||
collecting application performance and behavior monitoring data.
|
||||
Currently it consists of three major components: tags, stats and tracing.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get -u go.opencensus.io
|
||||
```
|
||||
|
||||
The API of this project is still evolving, see: [Deprecation Policy](#deprecation-policy).
|
||||
The use of vendoring or a dependency management tool is recommended.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
OpenCensus Go libraries require Go 1.8 or later.
|
||||
|
||||
## Getting Started
|
||||
|
||||
The easiest way to get started using OpenCensus in your application is to use an existing
|
||||
integration with your RPC framework:
|
||||
|
||||
* [net/http](https://godoc.org/go.opencensus.io/plugin/ochttp)
|
||||
* [gRPC](https://godoc.org/go.opencensus.io/plugin/ocgrpc)
|
||||
* [database/sql](https://godoc.org/github.com/opencensus-integrations/ocsql)
|
||||
* [Go kit](https://godoc.org/github.com/go-kit/kit/tracing/opencensus)
|
||||
* [Groupcache](https://godoc.org/github.com/orijtech/groupcache)
|
||||
* [Caddy webserver](https://godoc.org/github.com/orijtech/caddy)
|
||||
* [MongoDB](https://godoc.org/github.com/orijtech/mongo-go-driver)
|
||||
* [Redis gomodule/redigo](https://godoc.org/github.com/orijtech/redigo)
|
||||
* [Redis goredis/redis](https://godoc.org/github.com/orijtech/redis)
|
||||
* [Memcache](https://godoc.org/github.com/orijtech/gomemcache)
|
||||
|
||||
If you're using a framework not listed here, you could either implement your own middleware for your
|
||||
framework or use [custom stats](#stats) and [spans](#spans) directly in your application.
|
||||
|
||||
## Exporters
|
||||
|
||||
OpenCensus can export instrumentation data to various backends.
|
||||
OpenCensus has exporter implementations for the following, users
|
||||
can implement their own exporters by implementing the exporter interfaces
|
||||
([stats](https://godoc.org/go.opencensus.io/stats/view#Exporter),
|
||||
[trace](https://godoc.org/go.opencensus.io/trace#Exporter)):
|
||||
|
||||
* [Prometheus][exporter-prom] for stats
|
||||
* [OpenZipkin][exporter-zipkin] for traces
|
||||
* [Stackdriver][exporter-stackdriver] Monitoring for stats and Trace for traces
|
||||
* [Jaeger][exporter-jaeger] for traces
|
||||
* [AWS X-Ray][exporter-xray] for traces
|
||||
* [Datadog][exporter-datadog] for stats and traces
|
||||
* [Graphite][exporter-graphite] for stats
|
||||
* [Honeycomb][exporter-honeycomb] for traces
|
||||
|
||||
## Overview
|
||||
|
||||
![OpenCensus Overview](https://i.imgur.com/cf4ElHE.jpg)
|
||||
|
||||
In a microservices environment, a user request may go through
|
||||
multiple services until there is a response. OpenCensus allows
|
||||
you to instrument your services and collect diagnostics data all
|
||||
through your services end-to-end.
|
||||
|
||||
## Tags
|
||||
|
||||
Tags represent propagated key-value pairs. They are propagated using `context.Context`
|
||||
in the same process or can be encoded to be transmitted on the wire. Usually, this will
|
||||
be handled by an integration plugin, e.g. `ocgrpc.ServerHandler` and `ocgrpc.ClientHandler`
|
||||
for gRPC.
|
||||
|
||||
Package `tag` allows adding or modifying tags in the current context.
|
||||
|
||||
[embedmd]:# (internal/readme/tags.go new)
|
||||
```go
|
||||
ctx, err = tag.New(ctx,
|
||||
tag.Insert(osKey, "macOS-10.12.5"),
|
||||
tag.Upsert(userIDKey, "cde36753ed"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
```
|
||||
|
||||
## Stats
|
||||
|
||||
OpenCensus is a low-overhead framework even if instrumentation is always enabled.
|
||||
In order to be so, it is optimized to make recording of data points fast
|
||||
and separate from the data aggregation.
|
||||
|
||||
OpenCensus stats collection happens in two stages:
|
||||
|
||||
* Definition of measures and recording of data points
|
||||
* Definition of views and aggregation of the recorded data
|
||||
|
||||
### Recording
|
||||
|
||||
Measurements are data points associated with a measure.
|
||||
Recording implicitly tags the set of Measurements with the tags from the
|
||||
provided context:
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go record)
|
||||
```go
|
||||
stats.Record(ctx, videoSize.M(102478))
|
||||
```
|
||||
|
||||
### Views
|
||||
|
||||
Views are how Measures are aggregated. You can think of them as queries over the
|
||||
set of recorded data points (measurements).
|
||||
|
||||
Views have two parts: the tags to group by and the aggregation type used.
|
||||
|
||||
Currently three types of aggregations are supported:
|
||||
* CountAggregation is used to count the number of times a sample was recorded.
|
||||
* DistributionAggregation is used to provide a histogram of the values of the samples.
|
||||
* SumAggregation is used to sum up all sample values.
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go aggs)
|
||||
```go
|
||||
distAgg := view.Distribution(1<<32, 2<<32, 3<<32)
|
||||
countAgg := view.Count()
|
||||
sumAgg := view.Sum()
|
||||
```
|
||||
|
||||
Here we create a view with the DistributionAggregation over our measure.
|
||||
|
||||
[embedmd]:# (internal/readme/stats.go view)
|
||||
```go
|
||||
if err := view.Register(&view.View{
|
||||
Name: "example.com/video_size_distribution",
|
||||
Description: "distribution of processed video size over time",
|
||||
Measure: videoSize,
|
||||
Aggregation: view.Distribution(1<<32, 2<<32, 3<<32),
|
||||
}); err != nil {
|
||||
log.Fatalf("Failed to register view: %v", err)
|
||||
}
|
||||
```
|
||||
|
||||
Register begins collecting data for the view. Registered views' data will be
|
||||
exported via the registered exporters.
|
||||
|
||||
## Traces
|
||||
|
||||
A distributed trace tracks the progression of a single user request as
|
||||
it is handled by the services and processes that make up an application.
|
||||
Each step is called a span in the trace. Spans include metadata about the step,
|
||||
including especially the time spent in the step, called the span’s latency.
|
||||
|
||||
Below you see a trace and several spans underneath it.
|
||||
|
||||
![Traces and spans](https://i.imgur.com/7hZwRVj.png)
|
||||
|
||||
### Spans
|
||||
|
||||
Span is the unit step in a trace. Each span has a name, latency, status and
|
||||
additional metadata.
|
||||
|
||||
Below we are starting a span for a cache read and ending it
|
||||
when we are done:
|
||||
|
||||
[embedmd]:# (internal/readme/trace.go startend)
|
||||
```go
|
||||
ctx, span := trace.StartSpan(ctx, "cache.Get")
|
||||
defer span.End()
|
||||
|
||||
// Do work to get from cache.
|
||||
```
|
||||
|
||||
### Propagation
|
||||
|
||||
Spans can have parents or can be root spans if they don't have any parents.
|
||||
The current span is propagated in-process and across the network to allow associating
|
||||
new child spans with the parent.
|
||||
|
||||
In the same process, `context.Context` is used to propagate spans.
|
||||
`trace.StartSpan` creates a new span as a root if the current context
|
||||
doesn't contain a span. Or, it creates a child of the span that is
|
||||
already in current context. The returned context can be used to keep
|
||||
propagating the newly created span in the current context.
|
||||
|
||||
[embedmd]:# (internal/readme/trace.go startend)
|
||||
```go
|
||||
ctx, span := trace.StartSpan(ctx, "cache.Get")
|
||||
defer span.End()
|
||||
|
||||
// Do work to get from cache.
|
||||
```
|
||||
|
||||
Across the network, OpenCensus provides different propagation
|
||||
methods for different protocols.
|
||||
|
||||
* gRPC integrations use the OpenCensus' [binary propagation format](https://godoc.org/go.opencensus.io/trace/propagation).
|
||||
* HTTP integrations use Zipkin's [B3](https://github.com/openzipkin/b3-propagation)
|
||||
by default but can be configured to use a custom propagation method by setting another
|
||||
[propagation.HTTPFormat](https://godoc.org/go.opencensus.io/trace/propagation#HTTPFormat).
|
||||
|
||||
## Execution Tracer
|
||||
|
||||
With Go 1.11, OpenCensus Go will support integration with the Go execution tracer.
|
||||
See [Debugging Latency in Go](https://medium.com/observability/debugging-latency-in-go-1-11-9f97a7910d68)
|
||||
for an example of their mutual use.
|
||||
|
||||
## Profiles
|
||||
|
||||
OpenCensus tags can be applied as profiler labels
|
||||
for users who are on Go 1.9 and above.
|
||||
|
||||
[embedmd]:# (internal/readme/tags.go profiler)
|
||||
```go
|
||||
ctx, err = tag.New(ctx,
|
||||
tag.Insert(osKey, "macOS-10.12.5"),
|
||||
tag.Insert(userIDKey, "fff0989878"),
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
tag.Do(ctx, func(ctx context.Context) {
|
||||
// Do work.
|
||||
// When profiling is on, samples will be
|
||||
// recorded with the key/values from the tag map.
|
||||
})
|
||||
```
|
||||
|
||||
A screenshot of the CPU profile from the program above:
|
||||
|
||||
![CPU profile](https://i.imgur.com/jBKjlkw.png)
|
||||
|
||||
## Deprecation Policy
|
||||
|
||||
Before version 1.0.0, the following deprecation policy will be observed:
|
||||
|
||||
No backwards-incompatible changes will be made except for the removal of symbols that have
|
||||
been marked as *Deprecated* for at least one minor release (e.g. 0.9.0 to 0.10.0). A release
|
||||
removing the *Deprecated* functionality will be made no sooner than 28 days after the first
|
||||
release in which the functionality was marked *Deprecated*.
|
||||
|
||||
[travis-image]: https://travis-ci.org/census-instrumentation/opencensus-go.svg?branch=master
|
||||
[travis-url]: https://travis-ci.org/census-instrumentation/opencensus-go
|
||||
[appveyor-image]: https://ci.appveyor.com/api/projects/status/vgtt29ps1783ig38?svg=true
|
||||
[appveyor-url]: https://ci.appveyor.com/project/opencensusgoteam/opencensus-go/branch/master
|
||||
[godoc-image]: https://godoc.org/go.opencensus.io?status.svg
|
||||
[godoc-url]: https://godoc.org/go.opencensus.io
|
||||
[gitter-image]: https://badges.gitter.im/census-instrumentation/lobby.svg
|
||||
[gitter-url]: https://gitter.im/census-instrumentation/lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
|
||||
|
||||
|
||||
[new-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap
|
||||
[new-replace-ex]: https://godoc.org/go.opencensus.io/tag#example-NewMap--Replace
|
||||
|
||||
[exporter-prom]: https://godoc.org/go.opencensus.io/exporter/prometheus
|
||||
[exporter-stackdriver]: https://godoc.org/contrib.go.opencensus.io/exporter/stackdriver
|
||||
[exporter-zipkin]: https://godoc.org/go.opencensus.io/exporter/zipkin
|
||||
[exporter-jaeger]: https://godoc.org/go.opencensus.io/exporter/jaeger
|
||||
[exporter-xray]: https://github.com/census-ecosystem/opencensus-go-exporter-aws
|
||||
[exporter-datadog]: https://github.com/DataDog/opencensus-go-exporter-datadog
|
||||
[exporter-graphite]: https://github.com/census-ecosystem/opencensus-go-exporter-graphite
|
||||
[exporter-honeycomb]: https://github.com/honeycombio/opencensus-exporter
|
25
vendor/go.opencensus.io/appveyor.yml
generated
vendored
Normal file
25
vendor/go.opencensus.io/appveyor.yml
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
version: "{build}"
|
||||
|
||||
platform: x64
|
||||
|
||||
clone_folder: c:\gopath\src\go.opencensus.io
|
||||
|
||||
environment:
|
||||
GOPATH: 'c:\gopath'
|
||||
GOVERSION: '1.11'
|
||||
GO111MODULE: 'on'
|
||||
CGO_ENABLED: '0' # See: https://github.com/appveyor/ci/issues/2613
|
||||
|
||||
install:
|
||||
- set PATH=%GOPATH%\bin;c:\go\bin;%PATH%
|
||||
- choco upgrade golang --version 1.11.5 # Temporary fix because of a go.sum bug in 1.11
|
||||
- go version
|
||||
- go env
|
||||
|
||||
build: false
|
||||
deploy: false
|
||||
|
||||
test_script:
|
||||
- cd %APPVEYOR_BUILD_FOLDER%
|
||||
- go build -v .\...
|
||||
- go test -v .\... # No -race because cgo is disabled
|
10
vendor/go.opencensus.io/go.mod
generated
vendored
Normal file
10
vendor/go.opencensus.io/go.mod
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
module go.opencensus.io
|
||||
|
||||
require (
|
||||
github.com/golang/protobuf v1.2.0
|
||||
github.com/google/go-cmp v0.2.0
|
||||
github.com/hashicorp/golang-lru v0.5.0
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 // indirect
|
||||
google.golang.org/grpc v1.19.0
|
||||
)
|
50
vendor/go.opencensus.io/go.sum
generated
vendored
Normal file
50
vendor/go.opencensus.io/go.sum
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19 h1:Lj2SnHtxkRGJDqnGaSjo+CCdIieEnwVazbOXILwQemk=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
37
vendor/go.opencensus.io/internal/internal.go
generated
vendored
Normal file
37
vendor/go.opencensus.io/internal/internal.go
generated
vendored
Normal file
|
@ -0,0 +1,37 @@
|
|||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal // import "go.opencensus.io/internal"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
opencensus "go.opencensus.io"
|
||||
)
|
||||
|
||||
// UserAgent is the user agent to be added to the outgoing
|
||||
// requests from the exporters.
|
||||
var UserAgent = fmt.Sprintf("opencensus-go/%s", opencensus.Version())
|
||||
|
||||
// MonotonicEndTime returns the end time at present
|
||||
// but offset from start, monotonically.
|
||||
//
|
||||
// The monotonic clock is used in subtractions hence
|
||||
// the duration since start added back to start gives
|
||||
// end as a monotonic time.
|
||||
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
|
||||
func MonotonicEndTime(start time.Time) time.Time {
|
||||
return start.Add(time.Now().Sub(start))
|
||||
}
|
50
vendor/go.opencensus.io/internal/sanitize.go
generated
vendored
Normal file
50
vendor/go.opencensus.io/internal/sanitize.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const labelKeySizeLimit = 100
|
||||
|
||||
// Sanitize returns a string that is trunacated to 100 characters if it's too
|
||||
// long, and replaces non-alphanumeric characters to underscores.
|
||||
func Sanitize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if len(s) > labelKeySizeLimit {
|
||||
s = s[:labelKeySizeLimit]
|
||||
}
|
||||
s = strings.Map(sanitizeRune, s)
|
||||
if unicode.IsDigit(rune(s[0])) {
|
||||
s = "key_" + s
|
||||
}
|
||||
if s[0] == '_' {
|
||||
s = "key" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// converts anything that is not a letter or digit to an underscore
|
||||
func sanitizeRune(r rune) rune {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
return r
|
||||
}
|
||||
// Everything else turns into an underscore
|
||||
return '_'
|
||||
}
|
75
vendor/go.opencensus.io/internal/tagencoding/tagencoding.go
generated
vendored
Normal file
75
vendor/go.opencensus.io/internal/tagencoding/tagencoding.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
// Package tagencoding contains the tag encoding
|
||||
// used interally by the stats collector.
|
||||
package tagencoding // import "go.opencensus.io/internal/tagencoding"
|
||||
|
||||
// Values represent the encoded buffer for the values.
|
||||
type Values struct {
|
||||
Buffer []byte
|
||||
WriteIndex int
|
||||
ReadIndex int
|
||||
}
|
||||
|
||||
func (vb *Values) growIfRequired(expected int) {
|
||||
if len(vb.Buffer)-vb.WriteIndex < expected {
|
||||
tmp := make([]byte, 2*(len(vb.Buffer)+1)+expected)
|
||||
copy(tmp, vb.Buffer)
|
||||
vb.Buffer = tmp
|
||||
}
|
||||
}
|
||||
|
||||
// WriteValue is the helper method to encode Values from map[Key][]byte.
|
||||
func (vb *Values) WriteValue(v []byte) {
|
||||
length := len(v) & 0xff
|
||||
vb.growIfRequired(1 + length)
|
||||
|
||||
// writing length of v
|
||||
vb.Buffer[vb.WriteIndex] = byte(length)
|
||||
vb.WriteIndex++
|
||||
|
||||
if length == 0 {
|
||||
// No value was encoded for this key
|
||||
return
|
||||
}
|
||||
|
||||
// writing v
|
||||
copy(vb.Buffer[vb.WriteIndex:], v[:length])
|
||||
vb.WriteIndex += length
|
||||
}
|
||||
|
||||
// ReadValue is the helper method to decode Values to a map[Key][]byte.
|
||||
func (vb *Values) ReadValue() []byte {
|
||||
// read length of v
|
||||
length := int(vb.Buffer[vb.ReadIndex])
|
||||
vb.ReadIndex++
|
||||
if length == 0 {
|
||||
// No value was encoded for this key
|
||||
return nil
|
||||
}
|
||||
|
||||
// read value of v
|
||||
v := make([]byte, length)
|
||||
endIdx := vb.ReadIndex + length
|
||||
copy(v, vb.Buffer[vb.ReadIndex:endIdx])
|
||||
vb.ReadIndex = endIdx
|
||||
return v
|
||||
}
|
||||
|
||||
// Bytes returns a reference to already written bytes in the Buffer.
|
||||
func (vb *Values) Bytes() []byte {
|
||||
return vb.Buffer[:vb.WriteIndex]
|
||||
}
|
53
vendor/go.opencensus.io/internal/traceinternals.go
generated
vendored
Normal file
53
vendor/go.opencensus.io/internal/traceinternals.go
generated
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright 2017, OpenCensus Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// Trace allows internal access to some trace functionality.
|
||||
// TODO(#412): remove this
|
||||
var Trace interface{}
|
||||
|
||||
// LocalSpanStoreEnabled true if the local span store is enabled.
|
||||
var LocalSpanStoreEnabled bool
|
||||
|
||||
// BucketConfiguration stores the number of samples to store for span buckets
|
||||
// for successful and failed spans for a particular span name.
|
||||
type BucketConfiguration struct {
|
||||
Name string
|
||||
MaxRequestsSucceeded int
|
||||
MaxRequestsErrors int
|
||||
}
|
||||
|
||||
// PerMethodSummary is a summary of the spans stored for a single span name.
|
||||
type PerMethodSummary struct {
|
||||
Active int
|
||||
LatencyBuckets []LatencyBucketSummary
|
||||
ErrorBuckets []ErrorBucketSummary
|
||||
}
|
||||
|
||||
// LatencyBucketSummary is a summary of a latency bucket.
|
||||
type LatencyBucketSummary struct {
|
||||
MinLatency, MaxLatency time.Duration
|
||||
Size int
|
||||
}
|
||||
|
||||
// ErrorBucketSummary is a summary of an error bucket.
|
||||
type ErrorBucketSummary struct {
|
||||
ErrorCode int32
|
||||
Size int
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue