Merge remote-tracking branch 'upstream/master' into advisory-fix-1

Signed-off-by: Bob Callaway <bcallaway@google.com>
This commit is contained in:
Bob Callaway
2022-09-26 15:15:58 -04:00
116 changed files with 2201 additions and 5520 deletions
+2 -2
View File
@@ -70,7 +70,7 @@ jobs:
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le
# cache-from: type=gha
# cache-to: type=gha,mode=max
push: ${{ github.event_name == 'push' }}
@@ -83,7 +83,7 @@ jobs:
labels: ${{ steps.meta.outputs.labels }}
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.6.0
uses: aquasecurity/trivy-action@0.7.1
with:
image-ref: "ghcr.io/dexidp/dex:${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.version'] }}"
format: "sarif"
+2 -2
View File
@@ -75,7 +75,7 @@ jobs:
uses: docker/build-push-action@v3
with:
context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64
platforms: linux/amd64,linux/arm/v7,linux/arm64,linux/ppc64le
# cache-from: type=gha
# cache-to: type=gha,mode=max
push: ${{ github.event_name == 'push' }}
@@ -96,7 +96,7 @@ jobs:
org.opencontainers.image.documentation=https://dexidp.io/docs/
- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@0.6.0
uses: aquasecurity/trivy-action@0.7.1
with:
image-ref: "ghcr.io/dexidp/dex:${{ steps.tags.outputs.version }}"
format: "template"
+5 -5
View File
@@ -1,6 +1,6 @@
ARG BASE_IMAGE=alpine
FROM golang:1.18.4-alpine3.15 AS builder
FROM golang:1.19.1-alpine3.16 AS builder
WORKDIR /usr/local/src/dex
@@ -22,26 +22,26 @@ COPY . .
RUN make release-binary
FROM alpine:3.16.1 AS stager
FROM alpine:3.16.2 AS stager
RUN mkdir -p /var/dex
RUN mkdir -p /etc/dex
COPY config.docker.yaml /etc/dex/
FROM alpine:3.16.1 AS gomplate
FROM alpine:3.16.2 AS gomplate
ARG TARGETOS
ARG TARGETARCH
ARG TARGETVARIANT
ENV GOMPLATE_VERSION=v3.10.0
ENV GOMPLATE_VERSION=v3.11.2
RUN wget -O /usr/local/bin/gomplate \
"https://github.com/hairyhenderson/gomplate/releases/download/${GOMPLATE_VERSION}/gomplate_${TARGETOS:-linux}-${TARGETARCH:-amd64}${TARGETVARIANT}" \
&& chmod +x /usr/local/bin/gomplate
# For Dependabot to detect base image versions
FROM alpine:3.16.1 AS alpine
FROM alpine:3.16.2 AS alpine
FROM gcr.io/distroless/static:latest AS distroless
FROM $BASE_IMAGE
+2 -2
View File
@@ -3,8 +3,8 @@ module github.com/dexidp/dex/api/v2
go 1.17
require (
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.0
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
)
require (
+4 -3
View File
@@ -117,8 +117,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -131,8 +131,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+6 -1
View File
@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"net/url"
"strings"
"github.com/dexidp/dex/connector"
"github.com/dexidp/dex/pkg/log"
@@ -69,7 +70,11 @@ func (m *callback) HandleCallback(s connector.Scopes, r *http.Request) (connecto
groups := m.groups
headerGroup := r.Header.Get(m.groupHeader)
if headerGroup != "" {
groups = append(groups, headerGroup)
splitheaderGroup := strings.Split(headerGroup, ",")
for i, v := range splitheaderGroup {
splitheaderGroup[i] = strings.TrimSpace(v)
}
groups = append(splitheaderGroup, groups...)
}
return connector.Identity{
UserID: remoteUser, // TODO: figure out if this is a bad ID value.
+134
View File
@@ -0,0 +1,134 @@
package authproxy
import (
"io"
"net/http"
"reflect"
"testing"
"github.com/sirupsen/logrus"
"github.com/dexidp/dex/connector"
)
const (
testEmail = "testuser@example.com"
testGroup1 = "group1"
testGroup2 = "group2"
testGroup3 = "group 3"
testGroup4 = "group 4"
testStaticGroup1 = "static1"
testStaticGroup2 = "static 2"
)
var logger = &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
func TestUser(t *testing.T) {
config := Config{
UserHeader: "X-Remote-User",
}
conn := callback{userHeader: config.UserHeader, logger: logger, pathSuffix: "/test"}
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
"X-Remote-User": {testEmail},
}
ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
expectEquals(t, ident.Email, testEmail)
expectEquals(t, len(ident.Groups), 0)
}
func TestSingleGroup(t *testing.T) {
config := Config{
UserHeader: "X-Remote-User",
GroupHeader: "X-Remote-Group",
}
conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, logger: logger, pathSuffix: "/test"}
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
"X-Remote-User": {testEmail},
"X-Remote-Group": {testGroup1},
}
ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
expectEquals(t, len(ident.Groups), 1)
expectEquals(t, ident.Groups[0], testGroup1)
}
func TestMultipleGroup(t *testing.T) {
config := Config{
UserHeader: "X-Remote-User",
GroupHeader: "X-Remote-Group",
}
conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, logger: logger, pathSuffix: "/test"}
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
"X-Remote-User": {testEmail},
"X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4},
}
ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
expectEquals(t, len(ident.Groups), 4)
expectEquals(t, ident.Groups[0], testGroup1)
expectEquals(t, ident.Groups[1], testGroup2)
expectEquals(t, ident.Groups[2], testGroup3)
expectEquals(t, ident.Groups[3], testGroup4)
}
func TestStaticGroup(t *testing.T) {
config := Config{
UserHeader: "X-Remote-User",
GroupHeader: "X-Remote-Group",
Groups: []string{"static1", "static 2"},
}
conn := callback{userHeader: config.UserHeader, groupHeader: config.GroupHeader, groups: config.Groups, logger: logger, pathSuffix: "/test"}
req, err := http.NewRequest("GET", "/", nil)
expectNil(t, err)
req.Header = map[string][]string{
"X-Remote-User": {testEmail},
"X-Remote-Group": {testGroup1 + ", " + testGroup2 + ", " + testGroup3 + ", " + testGroup4},
}
ident, err := conn.HandleCallback(connector.Scopes{OfflineAccess: true, Groups: true}, req)
expectNil(t, err)
expectEquals(t, ident.UserID, testEmail)
expectEquals(t, len(ident.Groups), 6)
expectEquals(t, ident.Groups[0], testGroup1)
expectEquals(t, ident.Groups[1], testGroup2)
expectEquals(t, ident.Groups[2], testGroup3)
expectEquals(t, ident.Groups[3], testGroup4)
expectEquals(t, ident.Groups[4], testStaticGroup1)
expectEquals(t, ident.Groups[5], testStaticGroup2)
}
func expectNil(t *testing.T, a interface{}) {
if a != nil {
t.Errorf("Expected %+v to equal nil", a)
}
}
func expectEquals(t *testing.T, a interface{}, b interface{}) {
if !reflect.DeepEqual(a, b) {
t.Errorf("Expected %+v to equal %+v", a, b)
}
}
+27 -27
View File
@@ -71,7 +71,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
scopes = append(scopes, "profile", "email")
}
srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail)
srv, err := createDirectoryService(c.ServiceAccountFilePath, c.AdminEmail, logger)
if err != nil {
cancel()
return nil, fmt.Errorf("could not create directory service: %v", err)
@@ -279,37 +279,37 @@ func (c *googleConnector) getGroups(email string, fetchTransitiveGroupMembership
return uniqueGroups(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) {
if serviceAccountFilePath == "" && email == "" {
return nil, nil
}
if serviceAccountFilePath == "" || email == "" {
return nil, fmt.Errorf("directory service requires both serviceAccountFilePath and adminEmail")
}
jsonCredentials, err := os.ReadFile(serviceAccountFilePath)
if err != nil {
return nil, fmt.Errorf("error reading credentials from file: %v", err)
// createDirectoryService sets up super user impersonation and creates an admin client for calling
// the google admin api. If no serviceAccountFilePath is defined, the application default credential
// is used.
func createDirectoryService(serviceAccountFilePath, email string, logger log.Logger) (*admin.Service, error) {
if email == "" {
return nil, fmt.Errorf("directory service requires adminEmail")
}
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
var jsonCredentials []byte
var err error
ctx := context.Background()
client := config.Client(ctx)
srv, err := admin.NewService(ctx, option.WithHTTPClient(client))
if err != nil {
return nil, fmt.Errorf("unable to create directory service %v", err)
if serviceAccountFilePath == "" {
logger.Warn("the application default credential is used since the service account file path is not used")
credential, err := google.FindDefaultCredentials(ctx)
if err != nil {
return nil, fmt.Errorf("failed to fetch application default credentials: %w", err)
}
jsonCredentials = credential.JSON
} else {
jsonCredentials, err = os.ReadFile(serviceAccountFilePath)
if err != nil {
return nil, fmt.Errorf("error reading credentials from file: %v", err)
}
}
return srv, nil
config, err := google.JWTConfigFromJSON(jsonCredentials, admin.AdminDirectoryGroupReadonlyScope)
if err != nil {
return nil, fmt.Errorf("unable to parse credentials to config: %v", err)
}
config.Subject = email
return admin.NewService(ctx, option.WithHTTPClient(config.Client(ctx)))
}
// uniqueGroups returns the unique groups of a slice
+145
View File
@@ -0,0 +1,145 @@
package google
import (
"encoding/json"
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func testSetup(t *testing.T) *httptest.Server {
mux := http.NewServeMux()
// TODO: mock calls
// mux.HandleFunc("/admin/directory/v1/groups", func(w http.ResponseWriter, r *http.Request) {
// w.Header().Add("Content-Type", "application/json")
// json.NewEncoder(w).Encode(&admin.Groups{
// Groups: []*admin.Group{},
// })
// })
return httptest.NewServer(mux)
}
func newConnector(config *Config, serverURL string) (*googleConnector, error) {
log := logrus.New()
conn, err := config.Open("id", log)
if err != nil {
return nil, err
}
googleConn, ok := conn.(*googleConnector)
if !ok {
return nil, fmt.Errorf("failed to convert to googleConnector")
}
return googleConn, nil
}
func tempServiceAccountKey() (string, error) {
fd, err := os.CreateTemp("", "google_service_account_key")
if err != nil {
return "", err
}
defer fd.Close()
err = json.NewEncoder(fd).Encode(map[string]string{
"type": "service_account",
"project_id": "sample-project",
"private_key_id": "sample-key-id",
"private_key": "-----BEGIN PRIVATE KEY-----\nsample-key\n-----END PRIVATE KEY-----\n",
"client_id": "sample-client-id",
"client_x509_cert_url": "localhost",
})
return fd.Name(), err
}
func TestOpen(t *testing.T) {
ts := testSetup(t)
defer ts.Close()
type testCase struct {
config *Config
expectedErr string
// string to set in GOOGLE_APPLICATION_CREDENTIALS. As local development environments can
// already contain ADC, test cases will be built uppon this setting this env variable
adc string
}
serviceAccountFilePath, err := tempServiceAccountKey()
assert.Nil(t, err)
for name, reference := range map[string]testCase{
"missing_admin_email": {
config: &Config{
ClientID: "testClient",
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
},
expectedErr: "requires adminEmail",
},
"service_account_key_not_found": {
config: &Config{
ClientID: "testClient",
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
AdminEmail: "foo@bar.com",
ServiceAccountFilePath: "not_found.json",
},
expectedErr: "error reading credentials",
},
"service_account_key_valid": {
config: &Config{
ClientID: "testClient",
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
AdminEmail: "foo@bar.com",
ServiceAccountFilePath: serviceAccountFilePath,
},
expectedErr: "",
},
"adc": {
config: &Config{
ClientID: "testClient",
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
AdminEmail: "foo@bar.com",
},
adc: serviceAccountFilePath,
expectedErr: "",
},
"adc_priority": {
config: &Config{
ClientID: "testClient",
ClientSecret: "testSecret",
RedirectURI: ts.URL + "/callback",
Scopes: []string{"openid", "groups"},
AdminEmail: "foo@bar.com",
ServiceAccountFilePath: serviceAccountFilePath,
},
adc: "/dev/null",
expectedErr: "",
},
} {
reference := reference
t.Run(name, func(t *testing.T) {
assert := assert.New(t)
os.Setenv("GOOGLE_APPLICATION_CREDENTIALS", reference.adc)
conn, err := newConnector(reference.config, ts.URL)
if reference.expectedErr == "" {
assert.Nil(err)
assert.NotNil(conn)
} else {
assert.ErrorContains(err, reference.expectedErr)
}
})
}
}
+11 -11
View File
@@ -18,6 +18,7 @@ type conn struct {
Host string
AdminUsername string
AdminPassword string
client *http.Client
Logger log.Logger
}
@@ -35,6 +36,7 @@ type domainKeystone struct {
// Config holds the configuration parameters for Keystone connector.
// Keystone should expose API v3
// An example config:
//
// connectors:
// type: keystone
// id: keystone
@@ -111,11 +113,12 @@ var (
// Open returns an authentication strategy using Keystone.
func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) {
return &conn{
c.Domain,
c.Host,
c.AdminUsername,
c.AdminPassword,
logger,
Domain: c.Domain,
Host: c.Host,
AdminUsername: c.AdminUsername,
AdminPassword: c.AdminPassword,
Logger: logger,
client: http.DefaultClient,
}, nil
}
@@ -192,7 +195,6 @@ func (p *conn) Refresh(
}
func (p *conn) getTokenResponse(ctx context.Context, username, pass string) (response *http.Response, err error) {
client := &http.Client{}
jsonData := loginRequestData{
auth: auth{
Identity: identity{
@@ -221,7 +223,7 @@ func (p *conn) getTokenResponse(ctx context.Context, username, pass string) (res
req.Header.Set("Content-Type", "application/json")
req = req.WithContext(ctx)
return client.Do(req)
return p.client.Do(req)
}
func (p *conn) getAdminToken(ctx context.Context) (string, error) {
@@ -243,7 +245,6 @@ func (p *conn) checkIfUserExists(ctx context.Context, userID string, token strin
func (p *conn) getUser(ctx context.Context, userID string, token string) (*userResponse, error) {
// https://developer.openstack.org/api-ref/identity/v3/#show-user-details
userURL := p.Host + "/v3/users/" + userID
client := &http.Client{}
req, err := http.NewRequest("GET", userURL, nil)
if err != nil {
return nil, err
@@ -251,7 +252,7 @@ func (p *conn) getUser(ctx context.Context, userID string, token string) (*userR
req.Header.Set("X-Auth-Token", token)
req = req.WithContext(ctx)
resp, err := client.Do(req)
resp, err := p.client.Do(req)
if err != nil {
return nil, err
}
@@ -276,7 +277,6 @@ func (p *conn) getUser(ctx context.Context, userID string, token string) (*userR
}
func (p *conn) getUserGroups(ctx context.Context, userID string, token string) ([]string, error) {
client := &http.Client{}
// https://developer.openstack.org/api-ref/identity/v3/#list-groups-to-which-a-user-belongs
groupsURL := p.Host + "/v3/users/" + userID + "/groups"
req, err := http.NewRequest("GET", groupsURL, nil)
@@ -285,7 +285,7 @@ func (p *conn) getUserGroups(ctx context.Context, userID string, token string) (
}
req.Header.Set("X-Auth-Token", token)
req = req.WithContext(ctx)
resp, err := client.Do(req)
resp, err := p.client.Do(req)
if err != nil {
p.Logger.Errorf("keystone: error while fetching user %q groups\n", userID)
return nil, err
+17 -17
View File
@@ -42,8 +42,6 @@ type groupResponse struct {
func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string) {
t.Helper()
client := &http.Client{}
jsonData := loginRequestData{
auth: auth{
Identity: identity{
@@ -70,7 +68,7 @@ func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string)
}
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -93,7 +91,6 @@ func getAdminToken(t *testing.T, adminName, adminPass string) (token, id string)
func createUser(t *testing.T, token, userName, userEmail, userPass string) string {
t.Helper()
client := &http.Client{}
createUserData := map[string]interface{}{
"user": map[string]interface{}{
@@ -116,7 +113,7 @@ func createUser(t *testing.T, token, userName, userEmail, userPass string) strin
}
req.Header.Set("X-Auth-Token", token)
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -139,7 +136,6 @@ func createUser(t *testing.T, token, userName, userEmail, userPass string) strin
// delete group or user
func deleteResource(t *testing.T, token, id, uri string) {
t.Helper()
client := &http.Client{}
deleteURI := uri + id
req, err := http.NewRequest("DELETE", deleteURI, nil)
@@ -148,7 +144,7 @@ func deleteResource(t *testing.T, token, id, uri string) {
}
req.Header.Set("X-Auth-Token", token)
resp, err := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("error: %v", err)
}
@@ -157,7 +153,6 @@ func deleteResource(t *testing.T, token, id, uri string) {
func createGroup(t *testing.T, token, description, name string) string {
t.Helper()
client := &http.Client{}
createGroupData := map[string]interface{}{
"group": map[string]interface{}{
@@ -177,7 +172,7 @@ func createGroup(t *testing.T, token, description, name string) string {
}
req.Header.Set("X-Auth-Token", token)
req.Header.Add("Content-Type", "application/json")
resp, err := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
@@ -200,14 +195,13 @@ func createGroup(t *testing.T, token, description, name string) string {
func addUserToGroup(t *testing.T, token, groupID, userID string) error {
t.Helper()
uri := groupsURL + groupID + "/users/" + userID
client := &http.Client{}
req, err := http.NewRequest("PUT", uri, nil)
if err != nil {
return err
}
req.Header.Set("X-Auth-Token", token)
resp, err := client.Do(req)
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatalf("error: %v", err)
}
@@ -219,7 +213,8 @@ func addUserToGroup(t *testing.T, token, groupID, userID string) error {
func TestIncorrectCredentialsLogin(t *testing.T) {
setupVariables(t)
c := conn{
Host: keystoneURL, Domain: testDomain,
client: http.DefaultClient,
Host: keystoneURL, Domain: testDomain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -296,7 +291,8 @@ func TestValidUserLogin(t *testing.T) {
defer deleteResource(t, token, userID, usersURL)
c := conn{
Host: keystoneURL, Domain: tt.input.domain,
client: http.DefaultClient,
Host: keystoneURL, Domain: tt.input.domain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -333,7 +329,8 @@ func TestUseRefreshToken(t *testing.T) {
defer deleteResource(t, token, groupID, groupsURL)
c := conn{
Host: keystoneURL, Domain: testDomain,
client: http.DefaultClient,
Host: keystoneURL, Domain: testDomain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -358,7 +355,8 @@ func TestUseRefreshTokenUserDeleted(t *testing.T) {
userID := createUser(t, token, testUser, testEmail, testPass)
c := conn{
Host: keystoneURL, Domain: testDomain,
client: http.DefaultClient,
Host: keystoneURL, Domain: testDomain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -388,7 +386,8 @@ func TestUseRefreshTokenGroupsChanged(t *testing.T) {
defer deleteResource(t, token, userID, usersURL)
c := conn{
Host: keystoneURL, Domain: testDomain,
client: http.DefaultClient,
Host: keystoneURL, Domain: testDomain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: true}
@@ -424,7 +423,8 @@ func TestNoGroupsInScope(t *testing.T) {
defer deleteResource(t, token, userID, usersURL)
c := conn{
Host: keystoneURL, Domain: testDomain,
client: http.DefaultClient,
Host: keystoneURL, Domain: testDomain,
AdminUsername: adminUser, AdminPassword: adminPass,
}
s := connector.Scopes{OfflineAccess: true, Groups: false}
+10 -1
View File
@@ -58,6 +58,8 @@ type Config struct {
// For valid values, see https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow#request-an-authorization-code.
PromptType string `json:"promptType"`
DomainHint string `json:"domainHint"`
Scopes []string `json:"scopes"` // defaults to scopeUser (user.read)
}
// Open returns a strategy for logging in through Microsoft.
@@ -77,6 +79,7 @@ func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error)
emailToLowercase: c.EmailToLowercase,
promptType: c.PromptType,
domainHint: c.DomainHint,
scopes: c.Scopes,
}
// By default allow logins from both personal and business/school
// accounts.
@@ -122,6 +125,7 @@ type microsoftConnector struct {
emailToLowercase bool
promptType string
domainHint string
scopes []string
}
func (c *microsoftConnector) isOrgTenant() bool {
@@ -133,7 +137,12 @@ func (c *microsoftConnector) groupsRequired(groupScope bool) bool {
}
func (c *microsoftConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config {
microsoftScopes := []string{scopeUser}
var microsoftScopes []string
if len(c.scopes) > 0 {
microsoftScopes = c.scopes
} else {
microsoftScopes = append(microsoftScopes, scopeUser)
}
if c.groupsRequired(scopes.Groups) {
microsoftScopes = append(microsoftScopes, scopeGroups)
}
+5
View File
@@ -351,6 +351,11 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
vs, found = claims[groupsKey].([]interface{})
}
// Fallback when claims[groupsKey] is a string instead of an array of strings.
if g, b := claims[groupsKey].(string); b {
groups = []string{g}
}
if found {
for _, v := range vs {
if s, ok := v.(string); ok {
+16
View File
@@ -271,6 +271,22 @@ func TestHandleCallback(t *testing.T) {
"cognito:groups": []string{"group3", "group4"},
},
},
{
name: "singularGroupResponseAsString",
userIDKey: "", // not configured
userNameKey: "", // not configured
expectUserID: "subvalue",
expectUserName: "namevalue",
expectGroups: []string{"group1"},
expectedEmailField: "emailvalue",
token: map[string]interface{}{
"sub": "subvalue",
"name": "namevalue",
"groups": "group1",
"email": "emailvalue",
"email_verified": true,
},
},
}
for _, tc := range tests {
Generated
+6 -6
View File
@@ -2,11 +2,11 @@
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1648297722,
"narHash": "sha256-W+qlPsiZd8F3XkzXOzAoR+mpFqzm3ekQkJNa+PIh1BQ=",
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "0f8662f1319ad6abf89b3380dd2722369fc51ade",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
@@ -17,11 +17,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1649225869,
"narHash": "sha256-u1zLtPmQzhT9mNXyM8Ey9pk7orDrIKdwooeGDEXm5xM=",
"lastModified": 1662019588,
"narHash": "sha256-oPEjHKGGVbBXqwwL+UjsveJzghWiWV0n9ogo1X6l4cw=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b6966d911da89e5a7301aaef8b4f0a44c77e103c",
"rev": "2da64a81275b68fdad38af669afeda43d401e94b",
"type": "github"
},
"original": {
+17 -15
View File
@@ -7,19 +7,21 @@
};
outputs = { self, nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
buildDeps = with pkgs; [ git go_1_18 gnumake ];
devDeps = with pkgs;
buildDeps ++ [
golangci-lint
gotestsum
protobuf
protoc-gen-go
protoc-gen-go-grpc
kind
];
in
{ devShell = pkgs.mkShell { buildInputs = devDeps; }; });
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = nixpkgs.legacyPackages.${system};
buildDeps = with pkgs; [ git go_1_19 gnumake ];
devDeps = with pkgs;
buildDeps ++ [
golangci-lint
gotestsum
protobuf
protoc-gen-go
protoc-gen-go-grpc
kind
];
in
{ devShell = pkgs.mkShell { buildInputs = devDeps; }; }
);
}
+20 -20
View File
@@ -1,48 +1,48 @@
module github.com/dexidp/dex
go 1.18
go 1.19
require (
entgo.io/ent v0.10.1
entgo.io/ent v0.11.2
github.com/AppsFlyer/go-sundheit v0.5.0
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig/v3 v3.2.2
github.com/beevik/etree v1.1.0
github.com/coreos/go-oidc/v3 v3.2.0
github.com/coreos/go-oidc/v3 v3.4.0
github.com/dexidp/dex/api/v2 v2.1.0
github.com/felixge/httpsnoop v1.0.3
github.com/ghodss/yaml v1.0.0
github.com/go-ldap/ldap/v3 v3.4.2
github.com/go-ldap/ldap/v3 v3.4.4
github.com/go-sql-driver/mysql v1.6.0
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/kylelemons/godebug v1.1.0
github.com/lib/pq v1.10.4
github.com/lib/pq v1.10.7
github.com/mattermost/xml-roundtrip-validator v0.1.0
github.com/mattn/go-sqlite3 v1.14.11
github.com/mattn/go-sqlite3 v1.14.15
github.com/oklog/run v1.1.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.12.2
github.com/prometheus/client_golang v1.13.0
github.com/russellhaering/goxmldsig v1.2.0
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.5.0
github.com/stretchr/testify v1.8.0
go.etcd.io/etcd/client/pkg/v3 v3.5.4
go.etcd.io/etcd/client/v3 v3.5.4
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2
google.golang.org/api v0.86.0
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
golang.org/x/net v0.0.0-20220909164309-bea034e7d591
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094
google.golang.org/api v0.97.0
google.golang.org/grpc v1.49.0
google.golang.org/protobuf v1.28.1
gopkg.in/square/go-jose.v2 v2.6.0
)
require (
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3 // indirect
ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e // indirect
cloud.google.com/go/compute v1.7.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c // indirect
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.1.1 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
@@ -52,7 +52,7 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.1 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/go-openapi/inflect v0.19.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
@@ -72,8 +72,8 @@ require (
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
@@ -83,8 +83,8 @@ require (
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.17.0 // indirect
golang.org/x/mod v0.5.1 // indirect
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f // indirect
+44 -36
View File
@@ -1,5 +1,5 @@
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3 h1:fjG4oFCQEfGrRi0QoxWcH2OO28CE6VYa6DkIr3yDySU=
ariga.io/atlas v0.3.7-0.20220303204946-787354f533c3/go.mod h1:yWGf4VPiD4SW83+kAqzD624txN9VKoJC+bpVXr2pKJA=
ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e h1:/r1xGMwmLg4LZ2V3/wWui9TtM3+STh1fp5ExSVRNFZo=
ariga.io/atlas v0.5.1-0.20220717122844-8593d7eb1a8e/go.mod h1:ofVetkJqlaWle3mvYmaS2uyFGFcc7dSq436tmxa/Mzk=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -57,12 +57,12 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
entgo.io/ent v0.10.1 h1:dM5h4Zk6yHGIgw4dCqVzGw3nWgpGYJiV4/kyHEF6PFo=
entgo.io/ent v0.10.1/go.mod h1:YPgxeLnoQ/YdpVORRtqjBF+wCy9NX9IR7veTv3Bffus=
entgo.io/ent v0.11.2 h1:UM2/BUhF2FfsxPHRxLjQbhqJNaDdVlOwNIAMLs2jyto=
entgo.io/ent v0.11.2/go.mod h1:YGHEQnmmIUgtD5b1ICD5vg74dS3npkNnmC5K+0J+IHU=
github.com/AppsFlyer/go-sundheit v0.5.0 h1:/VxpyigCfJrq1r97mn9HPiAB2qrhcTFHwNIIDr15CZM=
github.com/AppsFlyer/go-sundheit v0.5.0/go.mod h1:2ZM0BnfqT/mljBQO224VbL5XH06TgWuQ6Cn+cTtCpTY=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e h1:NeAW1fUYUEWhft7pkxDf6WoUvEZJ/uOKsvtpjLnn8MU=
github.com/Azure/go-ntlmssp v0.0.0-20220621081337-cb9428e4ac1e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DATA-DOG/go-sqlmock v1.5.0 h1:Shsta01QNfFxHCfpW6YH2STWB0MudeXXEWMr20OEh60=
@@ -111,8 +111,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/coreos/go-oidc/v3 v3.2.0 h1:2eR2MGR7thBXSQ2YbODlF0fcmgtliLCfr9iX6RW11fc=
github.com/coreos/go-oidc/v3 v3.2.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo=
github.com/coreos/go-oidc/v3 v3.4.0 h1:xz7elHb/LDwm/ERpwHd+5nb7wFHL32rsr6bBOgaeu6g=
github.com/coreos/go-oidc/v3 v3.4.0/go.mod h1:eHUXhZtXPQLgEaDrOVTgwbgmz1xGOkJNye6h3zkD2Pw=
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/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
@@ -140,19 +140,21 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-asn1-ber/asn1-ber v1.5.1 h1:pDbRAunXzIUXfx4CB2QJFv5IuPiuoW+sWvr/Us009o8=
github.com/go-asn1-ber/asn1-ber v1.5.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-ldap/ldap/v3 v3.4.2 h1:zFZKcXKLqZpFMrMQGHeHWKXbDTdNCmhGY9AK41zPh+8=
github.com/go-ldap/ldap/v3 v3.4.2/go.mod h1:iYS1MdmrmceOJ1QOTnRXrIs7i3kloqtmGQjRvjKpyMg=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-ldap/ldap/v3 v3.4.4 h1:qPjipEpt+qDa6SI/h1fzuGWoRUY+qqQ9sOZq67/PYUs=
github.com/go-ldap/ldap/v3 v3.4.4/go.mod h1:fe1MsuN5eJJ1FeLT/LEBVdWfNWKh459R7aXgXtJC+aI=
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-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4=
github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
@@ -298,12 +300,12 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v0.0.0-20170820004349-d65d576e9348/go.mod h1:B69LEHPfb2qLo0BaaOLcbitczOKLWTsrBG9LczfCD4k=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.10.7 h1:p7ZhMD+KsSRozJr34udlUrhboJwWAgCg34+/ZZNvZZw=
github.com/lib/pq v1.10.7/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
github.com/mattn/go-sqlite3 v1.14.11 h1:gt+cp9c0XGqe9S/wAHTL3n/7MqY+siPWgWJgqdsFrzQ=
github.com/mattn/go-sqlite3 v1.14.11/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ=
@@ -333,8 +335,9 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -343,14 +346,16 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
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.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
@@ -389,6 +394,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
@@ -431,10 +437,9 @@ golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200414173820-0848c9571904/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab h1:lnZ4LoV0UMdibeCUfIB2a4uFwRu491WX/VB2reB8xNc=
golang.org/x/crypto v0.0.0-20220208050332-20e1d8d225ab/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d h1:sK3txAijHtOK88l68nt020reeT1ZdKLIYetKl95FzVY=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -470,8 +475,8 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -495,7 +500,6 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -512,14 +516,17 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI=
golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
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/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -540,8 +547,8 @@ golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2 h1:+jnHzr9VPj32ykQVai5DNahi9+NSp7yYuCsl5eAQtL0=
golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 h1:2o1E+E8TpNLklK9nHiPiK1uzIYrIHt+cQx3ynCwq9V8=
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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=
@@ -623,9 +630,9 @@ golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10 h1:WIoqL4EROvwiPdUtaip4VcDdpZ4kha7wBWZrbVKCIZg=
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -741,8 +748,8 @@ google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69
google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
google.golang.org/api v0.86.0 h1:ZAnyOHQFIuWso1BodVfSaRyffD74T9ERGFa3k1fNk/U=
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
google.golang.org/api v0.97.0 h1:x/vEL1XDF/2V4xzdNgFPaKHluRESo2aTsL7QzHnBtGQ=
google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
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=
@@ -861,8 +868,9 @@ google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ5
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -877,8 +885,9 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -886,7 +895,6 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+39
View File
@@ -73,6 +73,17 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
clientID := r.Form.Get("client_id")
clientSecret := r.Form.Get("client_secret")
scopes := strings.Fields(r.Form.Get("scope"))
codeChallenge := r.Form.Get("code_challenge")
codeChallengeMethod := r.Form.Get("code_challenge_method")
if codeChallengeMethod == "" {
codeChallengeMethod = codeChallengeMethodPlain
}
if codeChallengeMethod != codeChallengeMethodS256 && codeChallengeMethod != codeChallengeMethodPlain {
description := fmt.Sprintf("Unsupported PKCE challenge method (%q).", codeChallengeMethod)
s.tokenErrHelper(w, errInvalidRequest, description, http.StatusBadRequest)
return
}
s.logger.Infof("Received device request for client %v with scopes %v", clientID, scopes)
@@ -108,6 +119,10 @@ func (s *Server) handleDeviceCode(w http.ResponseWriter, r *http.Request) {
Expiry: expireTime,
LastRequestTime: s.now(),
PollIntervalSeconds: 0,
PKCE: storage.PKCE{
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
},
}
if err := s.storage.CreateDeviceToken(deviceToken); err != nil {
@@ -236,6 +251,30 @@ func (s *Server) handleDeviceToken(w http.ResponseWriter, r *http.Request) {
s.tokenErrHelper(w, deviceTokenPending, "", http.StatusUnauthorized)
}
case deviceTokenComplete:
codeChallengeFromStorage := deviceToken.PKCE.CodeChallenge
providedCodeVerifier := r.Form.Get("code_verifier")
switch {
case providedCodeVerifier != "" && codeChallengeFromStorage != "":
calculatedCodeChallenge, err := s.calculateCodeChallenge(providedCodeVerifier, deviceToken.PKCE.CodeChallengeMethod)
if err != nil {
s.logger.Error(err)
s.tokenErrHelper(w, errServerError, "", http.StatusInternalServerError)
return
}
if codeChallengeFromStorage != calculatedCodeChallenge {
s.tokenErrHelper(w, errInvalidGrant, "Invalid code_verifier.", http.StatusBadRequest)
return
}
case providedCodeVerifier != "":
// Received no code_challenge on /auth, but a code_verifier on /token
s.tokenErrHelper(w, errInvalidRequest, "No PKCE flow started. Cannot check code_verifier.", http.StatusBadRequest)
return
case codeChallengeFromStorage != "":
// Received PKCE request on /auth, but no code_verifier on /token
s.tokenErrHelper(w, errInvalidGrant, "Expecting parameter code_verifier in PKCE flow.", http.StatusBadRequest)
return
}
w.Write([]byte(deviceToken.Token))
}
}
+126
View File
@@ -49,6 +49,7 @@ func TestHandleDeviceCode(t *testing.T) {
tests := []struct {
testName string
clientID string
codeChallengeMethod string
requestType string
scopes []string
expectedResponseCode int
@@ -71,6 +72,24 @@ func TestHandleDeviceCode(t *testing.T) {
expectedResponseCode: http.StatusBadRequest,
expectedContentType: "application/json",
},
{
testName: "New Code with valid PKCE",
clientID: "test",
requestType: "POST",
scopes: []string{"openid", "profile", "email"},
codeChallengeMethod: "S256",
expectedResponseCode: http.StatusOK,
expectedContentType: "application/json",
},
{
testName: "Invalid code challenge method",
clientID: "test",
requestType: "POST",
codeChallengeMethod: "invalid",
scopes: []string{"openid", "profile", "email"},
expectedResponseCode: http.StatusBadRequest,
expectedContentType: "application/json",
},
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
@@ -92,6 +111,7 @@ func TestHandleDeviceCode(t *testing.T) {
data := url.Values{}
data.Set("client_id", tc.clientID)
data.Set("code_challenge_method", tc.codeChallengeMethod)
for _, scope := range tc.scopes {
data.Add("scope", scope)
}
@@ -401,6 +421,13 @@ func TestDeviceTokenResponse(t *testing.T) {
now := func() time.Time { return t0 }
// Base PKCE values
// base64-urlencoded, sha256 digest of code_verifier
codeChallenge := "L7ZqsT_zNwvrH8E7J0CqPHx1wgBaFiaE-fAZcKUUAbc"
codeChallengeMethod := "S256"
// "random" string between 43 & 128 ASCII characters
codeVerifier := "66114650f56cc45dee7ee03c49f048ddf9aa53cbf5b09985832fa4f790ff2604"
baseDeviceRequest := storage.DeviceRequest{
UserCode: "ABCD-WXYZ",
DeviceCode: "foo",
@@ -415,6 +442,7 @@ func TestDeviceTokenResponse(t *testing.T) {
testDeviceToken storage.DeviceToken
testGrantType string
testDeviceCode string
testCodeVerifier string
expectedServerResponse string
expectedResponseCode int
}{
@@ -524,6 +552,101 @@ func TestDeviceTokenResponse(t *testing.T) {
expectedServerResponse: "{\"access_token\": \"foobar\"}",
expectedResponseCode: http.StatusOK,
},
{
testName: "Successful Exchange with PKCE",
testDeviceToken: storage.DeviceToken{
DeviceCode: "foo",
Status: deviceTokenComplete,
Token: "{\"access_token\": \"foobar\"}",
Expiry: now().Add(5 * time.Minute),
LastRequestTime: time.Time{},
PollIntervalSeconds: 0,
PKCE: storage.PKCE{
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
},
},
testDeviceCode: "foo",
testCodeVerifier: codeVerifier,
testDeviceRequest: baseDeviceRequest,
expectedServerResponse: "{\"access_token\": \"foobar\"}",
expectedResponseCode: http.StatusOK,
},
{
testName: "Test Exchange started with PKCE but without verifier provided",
testDeviceToken: storage.DeviceToken{
DeviceCode: "foo",
Status: deviceTokenComplete,
Token: "{\"access_token\": \"foobar\"}",
Expiry: now().Add(5 * time.Minute),
LastRequestTime: time.Time{},
PollIntervalSeconds: 0,
PKCE: storage.PKCE{
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
},
},
testDeviceCode: "foo",
testDeviceRequest: baseDeviceRequest,
expectedServerResponse: errInvalidGrant,
expectedResponseCode: http.StatusBadRequest,
},
{
testName: "Test Exchange not started with PKCE but verifier provided",
testDeviceToken: storage.DeviceToken{
DeviceCode: "foo",
Status: deviceTokenComplete,
Token: "{\"access_token\": \"foobar\"}",
Expiry: now().Add(5 * time.Minute),
LastRequestTime: time.Time{},
PollIntervalSeconds: 0,
},
testDeviceCode: "foo",
testCodeVerifier: codeVerifier,
testDeviceRequest: baseDeviceRequest,
expectedServerResponse: errInvalidRequest,
expectedResponseCode: http.StatusBadRequest,
},
{
testName: "Test with PKCE but incorrect verifier provided",
testDeviceToken: storage.DeviceToken{
DeviceCode: "foo",
Status: deviceTokenComplete,
Token: "{\"access_token\": \"foobar\"}",
Expiry: now().Add(5 * time.Minute),
LastRequestTime: time.Time{},
PollIntervalSeconds: 0,
PKCE: storage.PKCE{
CodeChallenge: codeChallenge,
CodeChallengeMethod: codeChallengeMethod,
},
},
testDeviceCode: "foo",
testCodeVerifier: "invalid",
testDeviceRequest: baseDeviceRequest,
expectedServerResponse: errInvalidGrant,
expectedResponseCode: http.StatusBadRequest,
},
{
testName: "Test with PKCE but incorrect challenge provided",
testDeviceToken: storage.DeviceToken{
DeviceCode: "foo",
Status: deviceTokenComplete,
Token: "{\"access_token\": \"foobar\"}",
Expiry: now().Add(5 * time.Minute),
LastRequestTime: time.Time{},
PollIntervalSeconds: 0,
PKCE: storage.PKCE{
CodeChallenge: "invalid",
CodeChallengeMethod: codeChallengeMethod,
},
},
testDeviceCode: "foo",
testCodeVerifier: codeVerifier,
testDeviceRequest: baseDeviceRequest,
expectedServerResponse: errInvalidGrant,
expectedResponseCode: http.StatusBadRequest,
},
}
for _, tc := range tests {
t.Run(tc.testName, func(t *testing.T) {
@@ -558,6 +681,9 @@ func TestDeviceTokenResponse(t *testing.T) {
}
data.Set("grant_type", grantType)
data.Set("device_code", tc.testDeviceCode)
if tc.testCodeVerifier != "" {
data.Set("code_verifier", tc.testCodeVerifier)
}
req, _ := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")

Some files were not shown because too many files have changed in this diff Show More