diff --git a/.appveyor/appveyor.yml b/.appveyor/appveyor.yml
index 0b4b2728e..391c75eaf 100644
--- a/.appveyor/appveyor.yml
+++ b/.appveyor/appveyor.yml
@@ -1,4 +1,5 @@
skip_non_tags: true
+image: Visual Studio 2019
clone_folder: c:\gopath\src\github.com\versent\saml2aws
environment:
GOPATH: c:\gopath
@@ -6,7 +7,8 @@ environment:
secure: 3kWTz99Qj+ipyaR73CxcJeGRRbmk84MF2ERDu6MyY10cjHAi6s3AVZ2Ccoa+Ioyt
appName: saml2aws
install:
-- set PATH=C:\msys64\mingw64\bin;%PATH%
+- set PATH=C:\msys64\mingw64\bin;C:\go120\bin;%PATH%
+- set GOROOT=C:\go120
- ps: >-
$VerbosePreference = 'Continue'
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 000000000..6162e9cc4
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+.buildtemp
\ No newline at end of file
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 1bb13ea22..1e0337155 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -6,3 +6,9 @@ updates:
interval: "weekly"
labels:
- "type: dependencies"
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: "weekly"
+ labels:
+ - "type: dependencies"
diff --git a/.github/win-msi/out/.gitignore b/.github/win-msi/out/.gitignore
new file mode 100644
index 000000000..1287e9bd7
--- /dev/null
+++ b/.github/win-msi/out/.gitignore
@@ -0,0 +1,2 @@
+**
+!.gitignore
diff --git a/.github/win-msi/src/saml2aws.wxs b/.github/win-msi/src/saml2aws.wxs
new file mode 100644
index 000000000..f1b2c8571
--- /dev/null
+++ b/.github/win-msi/src/saml2aws.wxs
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.github/win-msi/wix.sh b/.github/win-msi/wix.sh
new file mode 100644
index 000000000..cbf8dc092
--- /dev/null
+++ b/.github/win-msi/wix.sh
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+candle src/saml2aws.wxs -dSaml2AwsVer=${VERSION} -o "out/"
+light -sval "out/saml2aws.wixobj" -o "out/saml2aws_${VERSION}_windows_amd64.msi"
diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
index bbb163c98..ae17d3730 100644
--- a/.github/workflows/go.yml
+++ b/.github/workflows/go.yml
@@ -2,12 +2,11 @@ name: Go
on:
push:
- branches: [ master ]
+ branches: [master]
pull_request:
- branches: [ master ]
+ branches: [master]
jobs:
-
build:
name: Build
runs-on: ${{ matrix.os }}
@@ -15,54 +14,97 @@ jobs:
matrix:
os: [ubuntu-latest, macOS-latest, macos-11]
steps:
+ - name: Set up Go 1.x
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.20.x
- - name: Set up Go 1.x
- uses: actions/setup-go@v2
- with:
- go-version: 1.17.x
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v4
- - name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ - name: Test
+ run: |
+ go test -v ./... -coverprofile=${{ matrix.os }}_coverage.txt -covermode=atomic
- - name: Test
- run: go test -v ./...
+ - name: Upload coverage report
+ uses: actions/upload-artifact@v3
+ with:
+ name: reports
+ path: ${{ matrix.os }}_coverage.txt
+ if-no-files-found: error
+ retention-days: 1
- - name: Install
- run: go install ./cmd/saml2aws
+ - name: Install
+ run: go install ./cmd/saml2aws
linting:
name: lint
runs-on: ubuntu-latest
steps:
+ - name: Set up Go 1.x
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.20.x
- - name: Set up Go 1.x
- uses: actions/setup-go@v2
- with:
- go-version: 1.17.x
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v4
- - name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ - name: golangci-lint
+ uses: golangci/golangci-lint-action@v3
+ with:
+ version: v1.53.2
+ args: --timeout=2m
- - name: golangci-lint
- uses: golangci/golangci-lint-action@v2
- with:
- version: v1.42.0
+ coverage:
+ name: coverage
+ permissions:
+ contents: read
+ runs-on: ubuntu-latest
+ needs: [build]
+ steps:
+ - uses: actions/checkout@v4
+ - name: Download coverage reports
+ uses: actions/download-artifact@v3
+ with:
+ name: reports
+ path: reports
+
+ - name: Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ directory: reports
+ flags: unittests
release-build:
name: release-build
- runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - ubuntu-20.04
+ - macos-latest
+ runs-on: ${{ matrix.os }}
steps:
+ - name: Set up Go 1.x
+ uses: actions/setup-go@v4
+ with:
+ go-version: 1.20.x
+
+ - name: Check out code into the Go module directory
+ uses: actions/checkout@v4
- - name: Set up Go 1.x
- uses: actions/setup-go@v2
- with:
- go-version: 1.17.x
+ - name: Install dependency required for linux builds
+ if: matrix.os == 'ubuntu-20.04'
+ run: sudo apt-get update && sudo apt-get install -y libudev-dev
- - name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ - name: GoReleaser
+ uses: goreleaser/goreleaser-action@v5
+ with:
+ version: latest
+ args: build --snapshot --clean --config .goreleaser.${{ matrix.os }}.yml
- - name: GoReleaser
- uses: goreleaser/goreleaser-action@v2
- with:
- version: latest
- args: build --snapshot --rm-dist
+ - name: Upload
+ uses: actions/upload-artifact@v3
+ with:
+ name: saml2aws
+ path: dist/
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b6d9847ef..5577d09bb 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,25 +4,113 @@ on:
push:
tags:
- '*'
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: The tag to run against. This trigger only runs the MSI builder.
+ required: true
jobs:
release:
name: release
- runs-on: macOS-latest
+ strategy:
+ # the goreleaser and the Github release API doesn't handle concurrent
+ # access well, so run goreleaser serially
+ max-parallel: 1
+ matrix:
+ os:
+ - ubuntu-latest
+ - ubuntu-20.04
+ - macos-latest
+ runs-on: ${{ matrix.os }}
+ if: github.event_name != 'workflow_dispatch'
+ permissions: write-all
steps:
- name: Set up Go 1.x
- uses: actions/setup-go@v2
+ uses: actions/setup-go@v4
with:
- go-version: 1.17.x
+ go-version: 1.20.x
- name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
+ - name: Install dependency required for linux builds
+ if: matrix.os == 'ubuntu-20.04'
+ run: sudo apt-get update && sudo apt-get install -y libudev-dev
+
+ - name: Add Lowercase Repository Name to Environment
+ run: |
+ echo REPOSITORY_NAME_LOWERCASE=$(echo ${{ github.repository }} | tr '[:upper:]' '[:lower:]') >> $GITHUB_ENV
+
+ - uses: "docker/login-action@v3"
+ if: matrix.os == 'ubuntu-20.04'
+ with:
+ registry: "ghcr.io"
+ username: "${{ github.actor }}"
+ password: "${{ secrets.GITHUB_TOKEN }}"
- name: GoReleaser
- uses: goreleaser/goreleaser-action@v2
+ uses: goreleaser/goreleaser-action@v5
with:
version: latest
- args: release --rm-dist
+ args: release --clean --config .goreleaser.${{ matrix.os }}.yml
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ IMAGE_NAME: ${{ env.REPOSITORY_NAME_LOWERCASE }}
+
+ windows-msi:
+ name: Build Windows MSI and upload to release
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ needs: [release]
+ if: >- # https://github.com/actions/runner/issues/491
+ always() &&
+ (needs.release.result == 'success' || needs.release.result == 'skipped')
+ env:
+ INSTALLER: ${{ github.workspace }}/.github/win-msi
+ BIN: ${{ github.workspace }}/.github/win-msi/src/bin
+ WIXIMG: dactiv/wix@sha256:17d232708589641f5632f9a1ff9463ad087b192cea7b8e6012d2b47ec6af5f6c
+ steps:
+ - name: Normalize tag values
+ run: |
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]] ; then
+ VER=${{ github.event.inputs.tag }}
+ else
+ VER=${GITHUB_REF/refs\/tags\//}
+ fi
+
+ VERSION=${VER//v}
+
+ echo "VER_TAG=$VER" >> $GITHUB_ENV
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+ echo "ASSET=saml2aws_${VERSION}_windows_amd64.zip" >> $GITHUB_ENV
+
+ - name: Check out code
+ uses: actions/checkout@v4
+
+ - name: Retrieve the release asset
+ id: asset
+ uses: robinraju/release-downloader@efa4cd07bd0195e6cc65e9e30c251b49ce4d3e51 # v1.8
+ with:
+ repository: ${{ github.repository }}
+ tag: ${{ env.VER_TAG }}
+ fileName: ${{ env.ASSET }}
+ out-file-path: ${{ env.BIN }}
+
+ - name: Unzip asset
+ working-directory: ${{ env.BIN }}
+ run: unzip "${ASSET}"
+
+ - name: Build MSI
+ run: |
+ # container does not run as root
+ chmod -R o+rw "${INSTALLER}"
+
+ cat "${INSTALLER}/wix.sh" | docker run --rm -i -e VERSION -v "${INSTALLER}:/wix" ${WIXIMG} /bin/sh
+
+ - name: Upload the asset to the release
+ uses: softprops/action-gh-release@de2c0eb89ae2a093876385947365aca7b0e5f844 # v0.1.15 / v1
+ with:
+ tag_name: ${{ env.VER_TAG }}
+ files: ${{ env.INSTALLER }}/out/*.msi
diff --git a/.gitignore b/.gitignore
index 186524f9f..dab5a8422 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ vendor
/package
/stage
coverage.txt
+coverage.xml
.ctags
.vscode
bin/
@@ -19,3 +20,4 @@ bin/
# direnv
.envrc
+.buildtemp
\ No newline at end of file
diff --git a/.golangci.yaml b/.golangci.yaml
index 8cd3c6a64..2d5a1d26d 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -2,13 +2,10 @@ linters:
disable-all: true
enable:
- goimports
- - deadcode
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- - structcheck
- typecheck
- unused
- - varcheck
diff --git a/.goreleaser.yml b/.goreleaser.macos-latest.yml
similarity index 82%
rename from .goreleaser.yml
rename to .goreleaser.macos-latest.yml
index 393f7c395..de65cbc4a 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.macos-latest.yml
@@ -10,9 +10,7 @@ builds:
ldflags:
- -s -w -X main.Version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
goos:
- - windows
- darwin
- - linux
goarch:
- amd64
- arm64
@@ -20,10 +18,9 @@ builds:
archives:
- format: tar.gz
wrap_in_directory: false
- format_overrides:
- - goos: windows
- format: zip
# remove README and LICENSE
files:
- LICENSE.md
- README.md
+checksum:
+ name_template: "{{ .ProjectName }}_{{ .Version }}_darwin_checksums.txt"
diff --git a/.goreleaser.ubuntu-20.04.yml b/.goreleaser.ubuntu-20.04.yml
new file mode 100644
index 000000000..497fe4787
--- /dev/null
+++ b/.goreleaser.ubuntu-20.04.yml
@@ -0,0 +1,94 @@
+---
+project_name: saml2aws-u2f
+
+builds:
+- id: saml2aws
+ main: ./cmd/saml2aws/main.go
+ binary: saml2aws
+ flags:
+ - -trimpath
+ - -v
+ ldflags:
+ - -s -w -X main.Version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
+ goos:
+ - linux
+ goarch:
+ - amd64
+ overrides:
+ - goos: linux
+ goarch: amd64
+ goamd64: v1
+ tags:
+ - hidraw
+ env:
+ - CGO_ENABLED=1
+- id: saml2aws-static
+ main: ./cmd/saml2aws/main.go
+ binary: saml2aws
+ flags:
+ - -trimpath
+ - -v
+ ldflags:
+ - -s -w -X main.Version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}} -extldflags "-static"
+ goos:
+ - linux
+ goarch:
+ - amd64
+ - arm64
+ - arm
+ env:
+ - CGO_ENABLED=0
+archives:
+ - id: saml2aws
+ format: tar.gz
+ builds: [saml2aws]
+ wrap_in_directory: false
+ # remove README and LICENSE
+ files:
+ - LICENSE.md
+ - README.md
+ - id: saml2aws-static
+ format: tar.gz
+ builds: [saml2aws-static]
+ wrap_in_directory: false
+ # remove README and LICENSE
+ files:
+ - LICENSE.md
+ - README.md
+ name_template: "{{ .ProjectName }}_static_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
+checksum:
+ name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
+dockers:
+ - id: amd64
+ goos: linux
+ goarch: amd64
+ use: buildx
+ ids:
+ - saml2aws-static
+ image_templates:
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:{{ .Version }}-amd64
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:latest-amd64
+ build_flag_templates:
+ - "--build-arg=BASE_IMAGE_ARCH=static-debian11"
+ - "--platform=linux/amd64"
+ - id: arm64
+ goos: linux
+ goarch: arm64
+ use: buildx
+ ids:
+ - saml2aws-static
+ image_templates:
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:{{ .Version }}-arm64
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:latest-arm64
+ build_flag_templates:
+ - "--build-arg=BASE_IMAGE_ARCH=static:latest-arm64"
+ - "--platform=linux/arm64"
+docker_manifests:
+ - name_template: ghcr.io/{{ .Env.IMAGE_NAME }}:{{ .Version }}
+ image_templates:
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:{{ .Version }}-amd64
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:{{ .Version }}-arm64
+ - name_template: ghcr.io/{{ .Env.IMAGE_NAME }}:latest
+ image_templates:
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:latest-amd64
+ - ghcr.io/{{ .Env.IMAGE_NAME }}:latest-arm64
\ No newline at end of file
diff --git a/.goreleaser.ubuntu-latest.yml b/.goreleaser.ubuntu-latest.yml
new file mode 100644
index 000000000..5db45000c
--- /dev/null
+++ b/.goreleaser.ubuntu-latest.yml
@@ -0,0 +1,36 @@
+---
+project_name: saml2aws
+
+builds:
+- main: ./cmd/saml2aws/main.go
+ binary: saml2aws
+ flags:
+ - -trimpath
+ - -v
+ ldflags:
+ - -s -w -X main.Version={{.Version}} -X main.commit={{.Commit}} -X main.date={{.Date}}
+ goos:
+ - windows
+ - linux
+ goarch:
+ - amd64
+ - arm64
+ - arm
+ overrides:
+ - goos: linux
+ goarch: amd64
+ goamd64: v1
+ env:
+ - CGO_ENABLED=0
+archives:
+ - format: tar.gz
+ wrap_in_directory: false
+ format_overrides:
+ - goos: windows
+ format: zip
+ # remove README and LICENSE
+ files:
+ - LICENSE.md
+ - README.md
+checksum:
+ name_template: "{{ .ProjectName }}_{{ .Version }}_checksums.txt"
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 000000000..5a030a50e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,4 @@
+ARG BASE_IMAGE_ARCH=static-debian11
+FROM gcr.io/distroless/$BASE_IMAGE_ARCH
+COPY saml2aws /
+ENTRYPOINT ["/saml2aws"]
\ No newline at end of file
diff --git a/Dockerfile.build b/Dockerfile.build
new file mode 100644
index 000000000..8ba3ea493
--- /dev/null
+++ b/Dockerfile.build
@@ -0,0 +1,37 @@
+# base image
+FROM ubuntu:jammy
+ENV TZ=Australia/Sydney
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+
+# add arm64 architecture
+RUN apt-get update
+RUN dpkg --add-architecture arm64
+
+## arch-qualify the current repositories
+RUN sed -i "s/deb h/deb [arch=amd64] h/g" /etc/apt/sources.list
+
+## add arm64's repos
+RUN echo "# arm64 repositories" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy universe" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates universe" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy multiverse" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates multiverse" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-backports main restricted universe multiverse" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security main restricted" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security universe" >> /etc/apt/sources.list
+RUN echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-security multiverse" >> /etc/apt/sources.list
+
+RUN apt-get update && apt-get install -y build-essential git ca-certificates golang libudev-dev curl gnupg lsb-release curl gcc-arm* binutils-arm-linux-gnueabi crossbuild-essential-arm64 wget
+
+RUN echo \
+ "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
+ $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
+RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+RUN apt-get update && apt-get install -y docker-ce-cli
+# Replicate install of the same version of Golang that we are using in Github actions
+RUN wget https://go.dev/dl/go1.20.2.linux-amd64.tar.gz && tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz && rm go1.20.2.linux-amd64.tar.gz
+ENV GOROOT="/usr/local/go"
+ENV PATH="/root/go/bin:$GOROOT/bin:${PATH}"
+RUN go install github.com/goreleaser/goreleaser@v1.20.0 # supports golang 1.20
diff --git a/Makefile b/Makefile
index bb31ec9c6..9d81af104 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,10 @@
NAME=saml2aws
ARCH=$(shell uname -m)
-VERSION=2.28.0
+OS?=$(shell uname)
ITERATION := 1
-GOLANGCI_VERSION = 1.32.0
-GORELEASER_VERSION = 0.157.0
+GOLANGCI_VERSION = 1.53.2
+GORELEASER := $(shell command -v goreleaser 2> /dev/null)
SOURCE_FILES?=$$(go list ./... | grep -v /vendor/)
TEST_PATTERN?=.
@@ -12,33 +12,30 @@ TEST_OPTIONS?=
BIN_DIR := $(CURDIR)/bin
-ci: prepare test
-
-$(BIN_DIR)/golangci-lint: $(BIN_DIR)/golangci-lint-${GOLANGCI_VERSION}
- @ln -sf golangci-lint-${GOLANGCI_VERSION} $(BIN_DIR)/golangci-lint
-$(BIN_DIR)/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_DIR)/golangci-lint $@
+# Choose the right config file for the OS
+ifeq ($(OS),Darwin)
+ CONFIG_FILE?=$(CURDIR)/.goreleaser.macos-latest.yml
+else ifeq ($(OS),Linux)
+ CONFIG_FILE?=$(CURDIR)/.goreleaser.ubuntu-20.04.yml
+else
+ $(error Unsupported build OS: $(OS))
+endif
-$(BIN_DIR)/goreleaser: $(BIN_DIR)/goreleaser-${GORELEASER_VERSION}
- @ln -sf goreleaser-${GORELEASER_VERSION} $(BIN_DIR)/goreleaser
-$(BIN_DIR)/goreleaser-${GORELEASER_VERSION}:
- @curl -sfL https://install.goreleaser.com/github.com/goreleaser/goreleaser.sh | BINARY=goreleaser bash -s -- v${GORELEASER_VERSION}
- @mv $(BIN_DIR)/goreleaser $@
+ci: prepare test
mod:
@go mod download
@go mod tidy
.PHONY: mod
-lint: $(BIN_DIR)/golangci-lint
+lint:
@echo "--- lint all the things"
- @$(BIN_DIR)/golangci-lint run ./...
+ @docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v$(GOLANGCI_VERSION) golangci-lint run -v
.PHONY: lint
-lint-fix: $(BIN_DIR)/golangci-lint
+lint-fix:
@echo "--- lint all the things"
- @$(BIN_DIR)/golangci-lint run --fix ./...
+ @docker run --rm -v $(shell pwd):/app -w /app golangci/golangci-lint:v$(GOLANGCI_VERSION) golangci-lint run -v --fix
.PHONY: lint-fix
fmt: lint-fix
@@ -47,10 +44,18 @@ install:
go install ./cmd/saml2aws
.PHONY: mod
-build: $(BIN_DIR)/goreleaser
- $(BIN_DIR)/goreleaser build --snapshot --rm-dist
+build:
+
+ifndef GORELEASER
+ $(error "goreleaser is not available please install and ensure it is on PATH")
+endif
+ goreleaser build --snapshot --clean --config $(CONFIG_FILE)
.PHONY: build
+release-local: $(BIN_DIR)/goreleaser
+ goreleaser release --snapshot --rm-dist --config $(CONFIG_FILE)
+.PHONY: release-local
+
clean:
@rm -fr ./build
.PHONY: clean
@@ -64,3 +69,15 @@ test:
@echo "--- test all the things"
@go test -cover ./...
.PHONY: test
+
+# It can be difficult to set up and test everything locally. Using this target you can build and run a docker container
+# that has all the tools you need to build and test saml2aws. This is particularly useful on Mac as it allows the Linux
+# and Docker builds to be tested.
+# Note: By necessity, this target mounts the Docker socket into the container. This is a security risk and should not
+# be used on a production system.
+# Note: Files written by the container will be owned by root. This is a limitation of the Docker socket mount.
+# You may need to run `docker run --privileged --rm tonistiigi/binfmt --install all` to enable the buildx plugin.
+docker-build-environment:
+ docker build --platform=amd64 -t saml2aws/build -f Dockerfile.build .
+ docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock -e BUILDX_CONFIG=$(PWD)/.buildtemp -e GOPATH=$(PWD)/.buildtemp -e GOTMPDIR=$(PWD)/.buildtemp -e GOCACHE=$(PWD)/.buildtemp/.cache -e GOENV=$(PWD)/.buildtemp/env -v $(PWD):$(PWD) -w $(PWD) saml2aws/build:latest
+.PHONY: docker-build-environment
\ No newline at end of file
diff --git a/README.md b/README.md
index 4fda2277c..765d9bf74 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,7 @@
-# saml2aws [![GitHub Actions status](https://github.com/Versent/saml2aws/workflows/Go/badge.svg?branch=master)](https://github.com/Versent/saml2aws/actions?query=workflow%3AGo) [![Build status - Windows](https://ci.appveyor.com/api/projects/status/ptpi18kci16o4i82/branch/master?svg=true)](https://ci.appveyor.com/project/davidobrien1985/saml2aws/branch/master)
+# saml2aws
+
+[![GitHub Actions status](https://github.com/Versent/saml2aws/workflows/Go/badge.svg?branch=master)](https://github.com/Versent/saml2aws/actions?query=workflow%3AGo) [![Build status - Windows](https://ci.appveyor.com/api/projects/status/ptpi18kci16o4i82/branch/master?svg=true)](https://ci.appveyor.com/project/davidobrien1985/saml2aws/branch/master)
+[![codecov](https://codecov.io/gh/Versent/saml2aws/branch/master/graph/badge.svg)](https://codecov.io/gh/Versent/saml2aws)
CLI tool which enables you to login and retrieve [AWS](https://aws.amazon.com/) temporary credentials using
with [ADFS](https://msdn.microsoft.com/en-us/library/bb897402.aspx) or [PingFederate](https://www.pingidentity.com/en/products/pingfederate.html) Identity Providers.
@@ -18,25 +21,47 @@ The process goes something like this:
## Table of Contents
-- [Table of Contents](#table-of-contents)
-- [Requirements](#requirements)
-- [Caveats](#caveats)
-- [Install](#install)
+- [saml2aws](#saml2aws)
+ - [Table of Contents](#table-of-contents)
+ - [Requirements](#requirements)
+ - [Caveats](#caveats)
+ - [Install](#install)
- [OSX](#osx)
- [Windows](#windows)
- [Linux](#linux)
-- [Autocomplete](#autocomplete)
-- [Dependency Setup](#dependency-setup)
-- [Usage](#usage)
+ - [Using Make](#using-make)
+ - [Arch Linux and its derivatives](#arch-linux-and-its-derivatives)
+ - [Void Linux](#void-linux)
+ - [Autocomplete](#autocomplete)
+ - [Bash](#bash)
+ - [Zsh](#zsh)
+ - [Dependency Setup](#dependency-setup)
+ - [Usage](#usage)
- [`saml2aws script`](#saml2aws-script)
+ - [`saml2aws exec`](#saml2aws-exec)
- [Configuring IDP Accounts](#configuring-idp-accounts)
-- [Example](#example)
-- [Advanced Configuration](#advanced-configuration)
- - [Dev Account Setup](#dev-account-setup)
- - [Test Account Setup](#test-account-setup)
-- [Building](#building)
-- [Environment vars](#environment-vars)
-- [Provider Specific Documentation](#provider-specific-documentation)
+ - [Example](#example)
+ - [Advanced Configuration](#advanced-configuration)
+ - [Windows Subsystem Linux (WSL) Configuration](#windows-subsystem-linux-wsl-configuration)
+ - [Option 1: Disable Keychain](#option-1-disable-keychain)
+ - [Option 2: Configure Pass to be the default keyring](#option-2-configure-pass-to-be-the-default-keyring)
+ - [Configuring Multiple Accounts](#configuring-multiple-accounts)
+ - [Dev Account Setup](#dev-account-setup)
+ - [Test Account Setup](#test-account-setup)
+ - [Advanced Configuration (Multiple AWS account access but SAML authenticate against a single 'SSO' AWS account)](#advanced-configuration-multiple-aws-account-access-but-saml-authenticate-against-a-single-sso-aws-account)
+ - [Advanced Configuration - additional parameters](#advanced-configuration---additional-parameters)
+ - [Building](#building)
+ - [macOS](#macos)
+ - [Linux](#linux-1)
+ - [Environment vars](#environment-vars)
+ - [Provider Specific Documentation](#provider-specific-documentation)
+- [Dependencies](#dependencies)
+- [Releasing](#releasing)
+- [Debugging Issues with IDPs](#debugging-issues-with-idps)
+- [Using saml2aws as credential process](#using-saml2aws-as-credential-process)
+- [Caching the saml2aws SAML assertion for immediate reuse](#caching-the-saml2aws-saml-assertion-for-immediate-reuse)
+- [Okta Sessions](#okta-sessions)
+- [License](#license)
## Requirements
@@ -52,7 +77,7 @@ The process goes something like this:
* [Akamai](pkg/provider/akamai/README.md)
* OneLogin
* NetIQ
- * Browser, this uses [playwright-go](github.com/mxschmitt/playwright-go) to run a sandbox chromium window.
+ * Browser, this uses [playwright-go](github.com/playwright-community/playwright-go) to run a sandbox chromium window.
* [Auth0](pkg/provider/auth0/README.md) NOTE: Currently, MFA not supported
* AWS SAML Provider configured
@@ -62,6 +87,7 @@ Aside from Okta, most of the providers in this project are using screen scraping
1. AWS defaults to session tokens being issued with a duration of up to 3600 seconds (1 hour), this can now be configured as per [Enable Federated API Access to your AWS Resources for up to 12 hours Using IAM Roles](https://aws.amazon.com/blogs/security/enable-federated-api-access-to-your-aws-resources-for-up-to-12-hours-using-iam-roles/) and `--session-duration` flag.
2. Every SAML provider is different, the login process, MFA support is pluggable and therefore some work may be needed to integrate with your identity server
+3. By default, the temporary security credentials returned **do not support SigV4A**. If you need SigV4A support then you must set the `AWS_STS_REGIONAL_ENDPOINTS` enviornment variable to `regional` when calling `saml2aws` so that [aws-sdk-go](https://github.com/aws/aws-sdk-go) uses a regional STS endpoint instead of the global one. See the note at the bottom of [Signing AWS API requests](https://docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html#signature-versions) and [AWS STS Regionalized endpoints](https://docs.aws.amazon.com/sdkref/latest/guide/feature-sts-regionalized-endpoints.html).
## Install
@@ -88,12 +114,30 @@ saml2aws --version
While brew is available for Linux you can also run the following without using a package manager.
```
+mkdir -p ~/.local/bin
CURRENT_VERSION=$(curl -Ls https://api.github.com/repos/Versent/saml2aws/releases/latest | grep 'tag_name' | cut -d'v' -f2 | cut -d'"' -f1)
-wget -c https://github.com/Versent/saml2aws/releases/download/v${CURRENT_VERSION}/saml2aws_${CURRENT_VERSION}_linux_amd64.tar.gz -O - | tar -xzv -C ~/.local/bin
+wget -c "https://github.com/Versent/saml2aws/releases/download/v${CURRENT_VERSION}/saml2aws_${CURRENT_VERSION}_linux_amd64.tar.gz" -O - | tar -xzv -C ~/.local/bin
chmod u+x ~/.local/bin/saml2aws
hash -r
saml2aws --version
```
+If U2F support is required then there are separate builds for this - use the following download URL instead:
+```
+wget -c "https://github.com/Versent/saml2aws/releases/download/v${CURRENT_VERSION}/saml2aws-u2f_${CURRENT_VERSION}_linux_amd64.tar.gz" -O - | tar -xzv -C ~/.local/bin
+```
+
+#### Using Make
+
+You will need [Go Tools](https://golang.org/doc/install) (you can check your package maintainer as well) installed and the [Go Lint tool](https://github.com/alecthomas/gometalinter)
+
+Clone this repo to your `$GOPATH/src` directory
+
+Now you can install by running
+
+```
+make
+make install
+```
#### [Arch Linux](https://archlinux.org/) and its derivatives
@@ -151,6 +195,10 @@ Flags:
-a, --idp-account="default" The name of the configured IDP account. (env: SAML2AWS_IDP_ACCOUNT)
--idp-provider=IDP-PROVIDER
The configured IDP provider. (env: SAML2AWS_IDP_PROVIDER)
+ --browser-type=BROWSER-TYPE
+ The browser type to use when IDP provider is set to 'Browser'. if not set 'chromium' will be used. (env: SAML2AWS_BROWSER_TYPE)
+ --browser-executable-path=BROWSER-EXECUTABLE-PATH
+ The browser full path when IDP provider is set to 'Browser'. If set, no browser download will be performed and the executable path will be used instead. (env: SAML2AWS_BROWSER_EXECUTABLE_PATH)
--mfa=MFA The name of the mfa. (env: SAML2AWS_MFA)
-s, --skip-verify Skip verification of server certificate. (env: SAML2AWS_SKIP_VERIFY)
--url=URL The URL of the SAML IDP server used to login. (env: SAML2AWS_URL)
@@ -178,6 +226,8 @@ Commands:
--client-secret=CLIENT-SECRET
OneLogin client secret, used to generate API access token. (env: ONELOGIN_CLIENT_SECRET)
--subdomain=SUBDOMAIN OneLogin subdomain of your company account. (env: ONELOGIN_SUBDOMAIN)
+ --mfa-ip-address=MFA-IP-ADDRESS
+ IP address whitelisting defined in OneLogin MFA policies. (env: ONELOGIN_MFA_IP_ADDRESS)
-p, --profile=PROFILE The AWS profile to save the temporary credentials. (env: SAML2AWS_PROFILE)
--resource-id=RESOURCE-ID F5APM SAML resource ID of your company account. (env: SAML2AWS_F5APM_RESOURCE_ID)
--config=CONFIG Path/filename of saml2aws config file (env: SAML2AWS_CONFIGFILE)
@@ -191,16 +241,19 @@ Commands:
-p, --profile=PROFILE The AWS profile to save the temporary credentials. (env: SAML2AWS_PROFILE)
--duo-mfa-option=DUO-MFA-OPTION
- The MFA option you want to use to authenticate with
+ The MFA option you want to use to authenticate (supported providers: okta)(env: SAML_DUO_MFA_OPTION)
--client-id=CLIENT-ID OneLogin client id, used to generate API access token. (env: ONELOGIN_CLIENT_ID)
--client-secret=CLIENT-SECRET
OneLogin client secret, used to generate API access token. (env: ONELOGIN_CLIENT_SECRET)
+ --mfa-ip-address=MFA-IP-ADDRESS
+ IP address whitelisting defined in OneLogin MFA policies. (env: ONELOGIN_MFA_IP_ADDRESS)
--force Refresh credentials even if not expired.
--credential-process Enables AWS Credential Process support by outputting credentials to STDOUT in a JSON message.
--credentials-file=CREDENTIALS-FILE
The file that will cache the credentials retrieved from AWS. When not specified, will use the default AWS credentials file location. (env: SAML2AWS_CREDENTIALS_FILE)
--cache-saml Caches the SAML response (env: SAML2AWS_CACHE_SAML)
--cache-file=CACHE-FILE The location of the SAML cache file (env: SAML2AWS_SAML_CACHE_FILE)
+ --download-browser-driver Automatically download browsers for Browser IDP. (env: SAML2AWS_AUTO_BROWSER_DOWNLOAD)
--disable-sessions Do not use Okta sessions. Uses Okta sessions by default. (env: SAML2AWS_OKTA_DISABLE_SESSIONS)
--disable-remember-device Do not remember Okta MFA device. Remembers MFA device by default. (env: SAML2AWS_OKTA_DISABLE_REMEMBER_DEVICE)
@@ -234,7 +287,7 @@ Commands:
Emit a script that will export environment variables.
-p, --profile=PROFILE The AWS profile to save the temporary credentials. (env: SAML2AWS_PROFILE)
- --shell=bash Type of shell environment. Options include: bash, powershell, fish, env
+ --shell=bash Type of shell environment. Options include: bash, /bin/sh, powershell, fish, env
--credentials-file=CREDENTIALS-FILE
The file that will cache the credentials retrieved from AWS. When not specified, will use the default AWS credentials file location. (env: SAML2AWS_CREDENTIALS_FILE)
@@ -254,7 +307,7 @@ export AWS_CREDENTIAL_EXPIRATION="2016-09-04T38:27:00Z00:00"
SAML2AWS_PROFILE=saml
```
-Powershell, and fish shells are supported as well.
+Powershell, sh and fish shells are supported as well.
Env is useful for all AWS SDK compatible tools that can source an env file. It is a powerful combo with docker and the `--env-file` parameter.
If you use `eval $(saml2aws script)` frequently, you may want to create a alias for it:
@@ -385,10 +438,65 @@ To use this credential, call the AWS CLI with the --profile option (e.g. aws --p
```
## Advanced Configuration
+### Windows Subsystem Linux (WSL) Configuration
+If you are using WSL1 or WSL2, you might get the following error when attempting to save the credentials into the keychain
-Configuring multiple accounts with custom role and profile in `~/.aws/config` with goal being isolation between infra code when deploying to these environments. This setup assumes you're using separate roles and probably AWS accounts for `dev` and `test` and is designed to help operations staff avoid accidentally deploying to the wrong AWS account in complex environments. Note that this method configures SAML authentication to each AWS account directly (in this case different AWS accounts). In the example below, separate authentication values are configured for AWS accounts 'profile=customer-dev/awsAccount=was 121234567890' and 'profile=customer-test/awsAccount=121234567891'
+```
+ No such interface “org.freedesktop.DBus.Properties” on object at path /
+```
+
+This happens because the preferred keyring back-end - uses the `gnome-keyring` by default - which requires X11 - and if you are not using Windows 11 with support for Linux GUI applications - this can be difficult without [configuring a X11 forward](https://stackoverflow.com/questions/61110603/how-to-set-up-working-x11-forwarding-on-wsl2).
+
+There are 2 preferred approaches to workaround this issue:
+
+#### Option 1: Disable Keychain
+You can apply the `--disable-keychain` flag when using both the `configure` and `login` commands. Using this flag means that your credentials (such as your password to your IDP, or in the case of Okta the Okta Session Token) will not save to your keychain - and be skipped entierly. This means you will be required to enter your username and password each time you invoke the `login` command.
+
+#### Option 2: Configure Pass to be the default keyring
+There are a few steps involved with this option - however this option will save your credentials (such as your password to your IDP, and session tokens etc) into the `pass`[https://www.passwordstore.org/] keyring. The `pass` keyring is the standard Unix password manager. This option was *heavily inspired* by a similar issue in [aws-vault](https://github.com/99designs/aws-vault/issues/683)
+
+To configure pass to be the default keyring the following steps will need to be completed (assuming you are using Ubuntu 20.04 LTS):
+
+1. Install the pass backend and update gnupg, which encrypts passwords
+```bash
+sudo apt-get update && sudo apt-get install -y pass gnupg
+```
+
+2. Generate a key with gpg (gnupg) and take note of your public key
+```bash
+gpg --gen-key
+```
+
+The output of the gpg command will output the something similar to the following:
+```
+public and secret key created and signed.
+
+pub rsa3072 2021-04-22 [SC] [expires: 2023-04-22]
+ 844E426A53A64C2A916CBD1F522014D5FDBF6E3D
+uid Meir Gabay
+sub rsa3072 2021-04-22 [E] [expires: 2023-04-22]
+```
+
+3. Create a storage key in pass from the previously generated public (pub) key
+```bash
+pass init
+```
+during the `init` process you'll be requested to enter the passphrase provided in step 2
+
+4. Now, configure `saml2aws` to use the `pass` keyring. This can be done by setting the `SAML2AWS_KEYRING_BACKEND` environment variable to be `pass`. You'll need to also set the `GPG_TTY` to your current tty which means you can set the variable to `"$( tty )"`
+
+which means the following can be added into your profile
+```
+export SAML2AWS_KEYRING_BACKEND=pass
+export GPG_TTY="$( tty )"
+```
+
+5. Profit! Now when you run login/configure commands - you'll be promoted once to enter your passphrase - and your credentials will be saved into your keyring!
-### Dev Account Setup
+
+### Configuring Multiple Accounts
+Configuring multiple accounts with custom role and profile in `~/.aws/config` with goal being isolation between infra code when deploying to these environments. This setup assumes you're using separate roles and probably AWS accounts for `dev` and `test` and is designed to help operations staff avoid accidentally deploying to the wrong AWS account in complex environments. Note that this method configures SAML authentication to each AWS account directly (in this case different AWS accounts). In the example below, separate authentication values are configured for AWS accounts 'profile=customer-dev/awsAccount=was 121234567890' and 'profile=customer-test/awsAccount=121234567891'
+#### Dev Account Setup
To setup the dev account run the following and enter URL, username and password, and assign a standard role to be automatically selected on login.
@@ -415,7 +523,7 @@ region = us-east-1
To use this you will need to export `AWS_DEFAULT_PROFILE=customer-dev` environment variable to target `dev`.
-### Test Account Setup
+#### Test Account Setup
To setup the test account run the following and enter URL, username and password.
@@ -442,6 +550,18 @@ region = us-east-1
To use this you will need to export `AWS_DEFAULT_PROFILE=customer-test` environment variable to target `test`.
+### Playwright Browser Drivers for Browser IDP
+
+If you are using the Browser Identity Provider, on first invocation of `saml2aws login` you need to remember to install
+the browser drivers in order for playwright-go to work. Otherwise you will see the following error message:
+
+`Error authenticating to IDP.: could not start driver: fork/exec ... no such file or directory`
+
+To install the drivers, you can:
+* Pass `--download-browser-driver` to `saml2aws login`
+* Set in your shell environment `SAML2AWS_AUTO_BROWSER_DOWNLOAD=true`
+* Set `download_browser_driver = true` in your saml2aws config file, i.e. `~/.saml2aws`
+
## Advanced Configuration (Multiple AWS account access but SAML authenticate against a single 'SSO' AWS account)
Example:
@@ -573,6 +693,8 @@ region = us-east-1
```
## Building
+### macOS
+
To build this software on osx clone to the repo to `$GOPATH/src/github.com/versent/saml2aws` and ensure you have `$GOPATH/bin` in your `$PATH`.
```
@@ -597,6 +719,26 @@ Before raising a PR please run the linter.
make lint-fix
```
+### Linux
+
+To build this software on Debian/Ubuntu, you need to install a build dependency:
+
+```
+sudo apt install libudev-dev
+```
+
+You also need [GoReleaser](https://github.com/goreleaser/goreleaser) installed, and the binary (or a symlink) in `bin/goreleaser`.
+
+```
+ln -s $(command -v goreleaser) bin/goreleaser
+```
+
+Then you can build:
+
+```
+make build
+```
+
## Environment vars
The exec sub command will export the following environment variables.
diff --git a/aws_account.go b/aws_account.go
index bdf7e83d6..ff28c3abf 100644
--- a/aws_account.go
+++ b/aws_account.go
@@ -3,7 +3,7 @@ package saml2aws
import (
"bytes"
"fmt"
- "io/ioutil"
+ "io"
"net/http"
"net/url"
@@ -24,7 +24,7 @@ func ParseAWSAccounts(audience string, samlAssertion string) ([]*AWSAccount, err
return nil, errors.Wrap(err, "error retrieving AWS login form")
}
- data, err := ioutil.ReadAll(res.Body)
+ data, err := io.ReadAll(res.Body)
if err != nil {
return nil, errors.Wrap(err, "error retrieving AWS login body")
}
diff --git a/aws_account_test.go b/aws_account_test.go
index 9ea4ec11c..b5c86c43b 100644
--- a/aws_account_test.go
+++ b/aws_account_test.go
@@ -1,14 +1,14 @@
package saml2aws
import (
- "io/ioutil"
+ "os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestExtractAWSAccounts(t *testing.T) {
- data, err := ioutil.ReadFile("testdata/saml.html")
+ data, err := os.ReadFile("testdata/saml.html")
assert.Nil(t, err)
accounts, err := ExtractAWSAccounts(data)
diff --git a/aws_role.go b/aws_role.go
index c843f9b8f..60ff2246b 100644
--- a/aws_role.go
+++ b/aws_role.go
@@ -2,6 +2,7 @@ package saml2aws
import (
"fmt"
+ "regexp"
"strings"
)
@@ -29,7 +30,8 @@ func ParseAWSRoles(roles []string) ([]*AWSRole, error) {
}
func parseRole(role string) (*AWSRole, error) {
- tokens := strings.Split(role, ",")
+ r, _ := regexp.Compile("arn:([^:\n]*):([^:\n]*):([^:\n]*):([^:\n]*):(([^:/\n]*)[:/])?([^:,\n]*)")
+ tokens := r.FindAllString(role, -1)
if len(tokens) != 2 {
return nil, fmt.Errorf("Invalid role string only %d tokens", len(tokens))
diff --git a/choco/VERIFICATION.txt b/choco/VERIFICATION.txt
index 02109c0a6..0b4e283e8 100644
--- a/choco/VERIFICATION.txt
+++ b/choco/VERIFICATION.txt
@@ -2,7 +2,7 @@ VERIFICATION
Verification is intended to assist the Chocolatey moderators and community
in verifying that this package's contents are trustworthy.
-The installer has been automatically built from source whch can be found on
+The installer has been automatically built from source which can be found on
and can be verified like this:
1. Download the release version from
@@ -11,4 +11,4 @@ and can be verified like this:
- Use chocolatey utility 'checksum.exe'
Compare the checksums there with the checksum of the local binary in C:\ProgramData\Chocolatey\lib\saml2aws\src\saml2aws.exe
-File 'LICENSE.txt' is obtained from .
\ No newline at end of file
+File 'LICENSE.txt' is obtained from .
diff --git a/cmd/saml2aws/commands/configure.go b/cmd/saml2aws/commands/configure.go
index feddd6f64..a8f9fc7c2 100644
--- a/cmd/saml2aws/commands/configure.go
+++ b/cmd/saml2aws/commands/configure.go
@@ -88,7 +88,7 @@ func storeCredentials(configFlags *flags.CommonFlags, account *cfg.IDPAccount) e
}
if account.Provider == onelogin.ProviderName {
if configFlags.ClientID == "" || configFlags.ClientSecret == "" {
- log.Println("OneLogin provider requires --client_id and --client_secret flags to be set.")
+ log.Println("OneLogin provider requires --client-id and --client-secret flags to be set.")
os.Exit(1)
}
if err := credentials.SaveCredentials(path.Join(account.URL, OneLoginOAuthPath), configFlags.ClientID, configFlags.ClientSecret); err != nil {
diff --git a/cmd/saml2aws/commands/console.go b/cmd/saml2aws/commands/console.go
index cf11bf0d3..70e1f20d6 100644
--- a/cmd/saml2aws/commands/console.go
+++ b/cmd/saml2aws/commands/console.go
@@ -3,7 +3,7 @@ package commands
import (
"encoding/json"
"fmt"
- "io/ioutil"
+ "io"
"log"
"net/http"
"net/url"
@@ -136,7 +136,7 @@ func federatedLogin(creds *awsconfig.AWSCredentials, consoleFlags *flags.Console
}
defer resp.Body.Close()
- body, err := ioutil.ReadAll(resp.Body)
+ body, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
diff --git a/cmd/saml2aws/commands/login.go b/cmd/saml2aws/commands/login.go
index eb80280e1..10804d8e4 100644
--- a/cmd/saml2aws/commands/login.go
+++ b/cmd/saml2aws/commands/login.go
@@ -219,6 +219,19 @@ func resolveLoginDetails(account *cfg.IDPAccount, loginFlags *flags.LoginExecFla
loginDetails.ClientSecret = loginFlags.CommonFlags.ClientSecret
}
+ // if you supply an mfa_ip_address in a flag or an IDP account it takes precedence
+ if account.MFAIPAddress != "" {
+ loginDetails.MFAIPAddress = account.MFAIPAddress
+ } else if loginFlags.CommonFlags.MFAIPAddress != "" {
+ loginDetails.MFAIPAddress = loginFlags.CommonFlags.MFAIPAddress
+ }
+
+ if loginFlags.DownloadBrowser {
+ loginDetails.DownloadBrowser = loginFlags.DownloadBrowser
+ } else if account.DownloadBrowser {
+ loginDetails.DownloadBrowser = account.DownloadBrowser
+ }
+
// log.Printf("loginDetails %+v", loginDetails)
// if skip prompt was passed just pass back the flag values
diff --git a/cmd/saml2aws/commands/login_darwin.go b/cmd/saml2aws/commands/login_darwin.go
index 14b4d9bbe..c2d036354 100644
--- a/cmd/saml2aws/commands/login_darwin.go
+++ b/cmd/saml2aws/commands/login_darwin.go
@@ -1,3 +1,4 @@
+//go:build darwin && cgo
// +build darwin,cgo
package commands
diff --git a/cmd/saml2aws/commands/login_linux.go b/cmd/saml2aws/commands/login_linux.go
index b051a0f79..94a4d30fb 100644
--- a/cmd/saml2aws/commands/login_linux.go
+++ b/cmd/saml2aws/commands/login_linux.go
@@ -1,12 +1,19 @@
package commands
import (
+ "os"
+
"github.com/versent/saml2aws/v2/helper/credentials"
"github.com/versent/saml2aws/v2/helper/linuxkeyring"
+ "github.com/versent/saml2aws/v2/pkg/cfg"
)
func init() {
- if keyringHelper, err := linuxkeyring.NewKeyringHelper(); err == nil {
+ c := linuxkeyring.Configuration{
+ Backend: os.Getenv(cfg.KeyringBackEnvironmentVariableName),
+ }
+
+ if keyringHelper, err := linuxkeyring.NewKeyringHelper(c); err == nil {
credentials.CurrentHelper = keyringHelper
}
}
diff --git a/cmd/saml2aws/commands/login_test.go b/cmd/saml2aws/commands/login_test.go
index 0e29f3640..bca0442cf 100644
--- a/cmd/saml2aws/commands/login_test.go
+++ b/cmd/saml2aws/commands/login_test.go
@@ -15,7 +15,7 @@ import (
func TestResolveLoginDetailsWithFlags(t *testing.T) {
- commonFlags := &flags.CommonFlags{URL: "https://id.example.com", Username: "wolfeidau", Password: "testtestlol", MFAToken: "123456", SkipPrompt: true}
+ commonFlags := &flags.CommonFlags{URL: "https://id.example.com", Username: "wolfeidau", Password: "testtestlol", MFAIPAddress: "127.0.0.1", MFAToken: "123456", SkipPrompt: true}
loginFlags := &flags.LoginExecFlags{CommonFlags: commonFlags}
idpa := &cfg.IDPAccount{
@@ -27,7 +27,7 @@ func TestResolveLoginDetailsWithFlags(t *testing.T) {
loginDetails, err := resolveLoginDetails(idpa, loginFlags)
assert.Empty(t, err)
- assert.Equal(t, &creds.LoginDetails{Username: "wolfeidau", Password: "testtestlol", URL: "https://id.example.com", MFAToken: "123456"}, loginDetails)
+ assert.Equal(t, &creds.LoginDetails{Username: "wolfeidau", Password: "testtestlol", URL: "https://id.example.com", MFAToken: "123456", MFAIPAddress: "127.0.0.1"}, loginDetails)
}
func TestOktaResolveLoginDetailsWithFlags(t *testing.T) {
diff --git a/cmd/saml2aws/commands/script.go b/cmd/saml2aws/commands/script.go
index 4a736e6b4..0f35b3dbd 100644
--- a/cmd/saml2aws/commands/script.go
+++ b/cmd/saml2aws/commands/script.go
@@ -20,6 +20,14 @@ export SAML2AWS_PROFILE={{ .ProfileName }}
export AWS_CREDENTIAL_EXPIRATION={{ .Expires.Format "2006-01-02T15:04:05Z07:00" }}
`
+const shTmpl = `export AWS_ACCESS_KEY_ID={{ .AWSAccessKey }}
+export AWS_SECRET_ACCESS_KEY={{ .AWSSecretKey }}
+export AWS_SESSION_TOKEN={{ .AWSSessionToken }}
+export AWS_SECURITY_TOKEN={{ .AWSSecurityToken }}
+export SAML2AWS_PROFILE={{ .ProfileName }}
+export AWS_CREDENTIAL_EXPIRATION={{ .Expires.Format "2006-01-02T15:04:05Z07:00" }}
+`
+
const fishTmpl = `set -gx AWS_ACCESS_KEY_ID {{ .AWSAccessKey }}
set -gx AWS_SECRET_ACCESS_KEY {{ .AWSSecretKey }}
set -gx AWS_SESSION_TOKEN {{ .AWSSessionToken }}
@@ -99,6 +107,8 @@ func buildTmpl(shell string, data interface{}) (string, error) {
switch shell {
case "bash":
t, err = t.Parse(bashTmpl)
+ case "/bin/sh":
+ t, err = t.Parse(shTmpl)
case "powershell":
t, err = t.Parse(powershellTmpl)
case "fish":
diff --git a/cmd/saml2aws/commands/script_test.go b/cmd/saml2aws/commands/script_test.go
index 0e44073c4..9d458584f 100644
--- a/cmd/saml2aws/commands/script_test.go
+++ b/cmd/saml2aws/commands/script_test.go
@@ -25,7 +25,40 @@ func TestBuildTmplBash(t *testing.T) {
}
st, err := buildTmpl("bash", data)
- assert.ErrorIs(t, err, nil)
+ assert.Nil(t, err)
+
+ expected := []string{
+ "export AWS_ACCESS_KEY_ID=access_key",
+ "export AWS_SECRET_ACCESS_KEY=secret_key",
+ "export AWS_SESSION_TOKEN=session_token",
+ "export AWS_SECURITY_TOKEN=security_token",
+ "export SAML2AWS_PROFILE=test_profile",
+ }
+
+ for _, test_string := range expected {
+ assert.Contains(t, st, test_string)
+ }
+
+}
+
+func TestBuildTmplSh(t *testing.T) {
+
+ data := struct {
+ ProfileName string
+ *awsconfig.AWSCredentials
+ }{
+ "test_profile",
+ &awsconfig.AWSCredentials{
+ AWSSecretKey: "secret_key",
+ AWSAccessKey: "access_key",
+ AWSSessionToken: "session_token",
+ AWSSecurityToken: "security_token",
+ Expires: time.Now(),
+ },
+ }
+
+ st, err := buildTmpl("/bin/sh", data)
+ assert.Nil(t, err)
expected := []string{
"export AWS_ACCESS_KEY_ID=access_key",
@@ -58,7 +91,7 @@ func TestBuildTmplFish(t *testing.T) {
}
st, err := buildTmpl("fish", data)
- assert.ErrorIs(t, err, nil)
+ assert.Nil(t, err)
expected := []string{
"set -gx AWS_ACCESS_KEY_ID access_key",
@@ -91,7 +124,7 @@ func TestBuildTmplEnv(t *testing.T) {
}
st, err := buildTmpl("env", data)
- assert.ErrorIs(t, err, nil)
+ assert.Nil(t, err)
expected := []string{
"AWS_ACCESS_KEY_ID=access_key",
diff --git a/cmd/saml2aws/main.go b/cmd/saml2aws/main.go
index d6ee181e3..1085bb69f 100644
--- a/cmd/saml2aws/main.go
+++ b/cmd/saml2aws/main.go
@@ -2,7 +2,7 @@ package main
import (
"crypto/tls"
- "io/ioutil"
+ "io"
"log"
"net/http"
"os"
@@ -12,6 +12,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/versent/saml2aws/v2/cmd/saml2aws/commands"
"github.com/versent/saml2aws/v2/pkg/flags"
+ "github.com/versent/saml2aws/v2/pkg/prompter"
)
var (
@@ -46,6 +47,7 @@ func buildCmdList(s kingpin.Settings) (target *[]string) {
func main() {
log.SetOutput(os.Stderr)
+ prompter.SetOutputWriter(os.Stderr)
log.SetFlags(0)
logrus.SetOutput(os.Stderr)
@@ -70,6 +72,8 @@ func main() {
app.Flag("config", "Path/filename of saml2aws config file (env: SAML2AWS_CONFIGFILE)").Envar("SAML2AWS_CONFIGFILE").StringVar(&commonFlags.ConfigFile)
app.Flag("idp-account", "The name of the configured IDP account. (env: SAML2AWS_IDP_ACCOUNT)").Envar("SAML2AWS_IDP_ACCOUNT").Short('a').Default("default").StringVar(&commonFlags.IdpAccount)
app.Flag("idp-provider", "The configured IDP provider. (env: SAML2AWS_IDP_PROVIDER)").Envar("SAML2AWS_IDP_PROVIDER").EnumVar(&commonFlags.IdpProvider, "Akamai", "AzureAD", "ADFS", "ADFS2", "Browser", "GoogleApps", "Ping", "JumpCloud", "Okta", "OneLogin", "PSU", "KeyCloak", "F5APM", "Shibboleth", "ShibbolethECP", "NetIQ", "Auth0")
+ app.Flag("browser-type", "The configured browser type when the IDP provider is set to Browser. if not set 'chromium' will be used. (env: SAML2AWS_BROWSER_TYPE)").Envar("SAML2AWS_BROWSER_TYPE").EnumVar(&commonFlags.BrowserType, "chromium", "firefox", "webkit", "chrome", "chrome-beta", "chrome-dev", "chrome-canary", "msedge", "msedge-beta", "msedge-dev", "msedge-canary")
+ app.Flag("browser-executable-path", "The configured browser full path when the IDP provider is set to Browser. If set, no browser download will be performed and the executable path will be used instead. (env: SAML2AWS_BROWSER_EXECUTABLE_PATH)").Envar("SAML2AWS_BROWSER_EXECUTABLE_PATH").StringVar(&commonFlags.BrowserExecutablePath)
app.Flag("mfa", "The name of the mfa. (env: SAML2AWS_MFA)").Envar("SAML2AWS_MFA").StringVar(&commonFlags.MFA)
app.Flag("skip-verify", "Skip verification of server certificate. (env: SAML2AWS_SKIP_VERIFY)").Envar("SAML2AWS_SKIP_VERIFY").Short('s').BoolVar(&commonFlags.SkipVerify)
app.Flag("url", "The URL of the SAML IDP server used to login. (env: SAML2AWS_URL)").Envar("SAML2AWS_URL").StringVar(&commonFlags.URL)
@@ -90,6 +94,7 @@ func main() {
cmdConfigure.Flag("client-id", "OneLogin client id, used to generate API access token. (env: ONELOGIN_CLIENT_ID)").Envar("ONELOGIN_CLIENT_ID").StringVar(&commonFlags.ClientID)
cmdConfigure.Flag("client-secret", "OneLogin client secret, used to generate API access token. (env: ONELOGIN_CLIENT_SECRET)").Envar("ONELOGIN_CLIENT_SECRET").StringVar(&commonFlags.ClientSecret)
cmdConfigure.Flag("subdomain", "OneLogin subdomain of your company account. (env: ONELOGIN_SUBDOMAIN)").Envar("ONELOGIN_SUBDOMAIN").StringVar(&commonFlags.Subdomain)
+ cmdConfigure.Flag("mfa-ip-address", "IP address whitelisting defined in OneLogin MFA policies. (env: ONELOGIN_MFA_IP_ADDRESS)").Envar("ONELOGIN_MFA_IP_ADDRESS").StringVar(&commonFlags.MFAIPAddress)
cmdConfigure.Flag("profile", "The AWS profile to save the temporary credentials. (env: SAML2AWS_PROFILE)").Envar("SAML2AWS_PROFILE").Short('p').StringVar(&commonFlags.Profile)
cmdConfigure.Flag("resource-id", "F5APM SAML resource ID of your company account. (env: SAML2AWS_F5APM_RESOURCE_ID)").Envar("SAML2AWS_F5APM_RESOURCE_ID").StringVar(&commonFlags.ResourceID)
cmdConfigure.Flag("credentials-file", "The file that will cache the credentials retrieved from AWS. When not specified, will use the default AWS credentials file location. (env: SAML2AWS_CREDENTIALS_FILE)").Envar("SAML2AWS_CREDENTIALS_FILE").StringVar(&commonFlags.CredentialsFile)
@@ -104,14 +109,16 @@ func main() {
loginFlags := new(flags.LoginExecFlags)
loginFlags.CommonFlags = commonFlags
cmdLogin.Flag("profile", "The AWS profile to save the temporary credentials. (env: SAML2AWS_PROFILE)").Short('p').Envar("SAML2AWS_PROFILE").StringVar(&commonFlags.Profile)
- cmdLogin.Flag("duo-mfa-option", "The MFA option you want to use to authenticate with").Envar("SAML2AWS_DUO_MFA_OPTION").EnumVar(&loginFlags.DuoMFAOption, "Passcode", "Phone Call", "Duo Push")
+ cmdLogin.Flag("duo-mfa-option", "The MFA option you want to use to authenticate with (supported providers: okta)").Envar("SAML2AWS_DUO_MFA_OPTION").EnumVar(&loginFlags.DuoMFAOption, "Passcode", "Duo Push")
cmdLogin.Flag("client-id", "OneLogin client id, used to generate API access token. (env: ONELOGIN_CLIENT_ID)").Envar("ONELOGIN_CLIENT_ID").StringVar(&commonFlags.ClientID)
cmdLogin.Flag("client-secret", "OneLogin client secret, used to generate API access token. (env: ONELOGIN_CLIENT_SECRET)").Envar("ONELOGIN_CLIENT_SECRET").StringVar(&commonFlags.ClientSecret)
+ cmdLogin.Flag("mfa-ip-address", "IP address whitelisting defined in OneLogin MFA policies. (env: ONELOGIN_MFA_IP_ADDRESS)").Envar("ONELOGIN_MFA_IP_ADDRESS").StringVar(&commonFlags.MFAIPAddress)
cmdLogin.Flag("force", "Refresh credentials even if not expired.").BoolVar(&loginFlags.Force)
cmdLogin.Flag("credential-process", "Enables AWS Credential Process support by outputting credentials to STDOUT in a JSON message.").BoolVar(&loginFlags.CredentialProcess)
cmdLogin.Flag("credentials-file", "The file that will cache the credentials retrieved from AWS. When not specified, will use the default AWS credentials file location. (env: SAML2AWS_CREDENTIALS_FILE)").Envar("SAML2AWS_CREDENTIALS_FILE").StringVar(&commonFlags.CredentialsFile)
cmdLogin.Flag("cache-saml", "Caches the SAML response (env: SAML2AWS_CACHE_SAML)").Envar("SAML2AWS_CACHE_SAML").BoolVar(&commonFlags.SAMLCache)
cmdLogin.Flag("cache-file", "The location of the SAML cache file (env: SAML2AWS_SAML_CACHE_FILE)").Envar("SAML2AWS_SAML_CACHE_FILE").StringVar(&commonFlags.SAMLCacheFile)
+ cmdLogin.Flag("download-browser-driver", "Automatically download browsers for Browser IDP. (env: SAML2AWS_AUTO_BROWSER_DOWNLOAD)").Envar("SAML2AWS_AUTO_BROWSER_DOWNLOAD").BoolVar(&loginFlags.DownloadBrowser)
cmdLogin.Flag("disable-sessions", "Do not use Okta sessions. Uses Okta sessions by default. (env: SAML2AWS_OKTA_DISABLE_SESSIONS)").Envar("SAML2AWS_OKTA_DISABLE_SESSIONS").BoolVar(&commonFlags.DisableSessions)
cmdLogin.Flag("disable-remember-device", "Do not remember Okta MFA device. Remembers MFA device by default. (env: SAML2AWS_OKTA_DISABLE_REMEMBER_DEVICE)").Envar("SAML2AWS_OKTA_DISABLE_REMEMBER_DEVICE").BoolVar(&commonFlags.DisableRememberDevice)
@@ -150,9 +157,9 @@ func main() {
cmdScript.Flag("credentials-file", "The file that will cache the credentials retrieved from AWS. When not specified, will use the default AWS credentials file location. (env: SAML2AWS_CREDENTIALS_FILE)").Envar("SAML2AWS_CREDENTIALS_FILE").StringVar(&commonFlags.CredentialsFile)
var shell string
cmdScript.
- Flag("shell", "Type of shell environment. Options include: bash, powershell, fish, env").
+ Flag("shell", "Type of shell environment. Options include: bash, /bin/sh, powershell, fish, env").
Default("bash").
- EnumVar(&shell, "bash", "powershell", "fish", "env")
+ EnumVar(&shell, "bash", "/bin/sh", "powershell", "fish", "env")
// Trigger the parsing of the command line inputs via kingpin
command := kingpin.MustParse(app.Parse(os.Args[1:]))
@@ -170,8 +177,8 @@ func main() {
}
if *quiet {
- log.SetOutput(ioutil.Discard)
- logrus.SetOutput(ioutil.Discard)
+ log.SetOutput(io.Discard)
+ logrus.SetOutput(io.Discard)
}
// Set the default transport settings so all http clients will pick them up.
diff --git a/coverage.xml b/coverage.xml
deleted file mode 100644
index 3f52527f0..000000000
--- a/coverage.xml
+++ /dev/null
@@ -1,11135 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/go.mod b/go.mod
index 169c09ce6..b7048eb4a 100644
--- a/go.mod
+++ b/go.mod
@@ -1,42 +1,46 @@
module github.com/versent/saml2aws/v2
-go 1.17
+go 1.20
require (
- github.com/99designs/keyring v1.1.6
- github.com/AlecAivazis/survey/v2 v2.3.2
+ github.com/99designs/keyring v1.2.2
+ github.com/AlecAivazis/survey/v2 v2.3.7
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e
- github.com/PuerkitoBio/goquery v1.8.0
+ github.com/PuerkitoBio/goquery v1.8.1
github.com/alecthomas/kingpin v2.2.6+incompatible
github.com/avast/retry-go v3.0.0+incompatible
- github.com/aws/aws-sdk-go v1.42.44
- github.com/beevik/etree v1.1.0
- github.com/danieljoos/wincred v1.1.2
- github.com/google/uuid v1.3.0
+ github.com/aws/aws-sdk-go v1.47.10
+ github.com/beevik/etree v1.2.0
+ github.com/danieljoos/wincred v1.2.0
+ github.com/google/uuid v1.4.0
+ github.com/h2non/gock v1.2.0
github.com/keybase/go-keychain v0.0.0-20211119201326-e02f34051621
github.com/marshallbrekka/go-u2fhost v0.0.0-20210111072507-3ccdec8c8105
github.com/mitchellh/go-homedir v1.1.0
- github.com/mxschmitt/playwright-go v0.1400.0
github.com/pkg/errors v0.9.1
- github.com/sirupsen/logrus v1.8.1
+ github.com/playwright-community/playwright-go v0.3900.1
+ github.com/sirupsen/logrus v1.9.3
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966
- github.com/stretchr/testify v1.7.0
- github.com/tidwall/gjson v1.13.0
- golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
- gopkg.in/ini.v1 v1.66.3
+ github.com/stretchr/testify v1.8.4
+ github.com/tidwall/gjson v1.17.0
+ golang.org/x/net v0.18.0
+ gopkg.in/ini.v1 v1.67.0
)
require (
+ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/bearsh/hid v1.3.0 // indirect
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
- github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b // indirect
+ github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
+ github.com/go-jose/go-jose/v3 v3.0.0 // indirect
+ github.com/go-stack/stack v1.8.1 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
- github.com/gorilla/websocket v1.4.2 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
+ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/mattn/go-colorable v0.1.2 // indirect
@@ -44,13 +48,13 @@ require (
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/stretchr/objx v0.2.0 // indirect
+ github.com/stretchr/objx v0.5.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
- github.com/tidwall/pretty v1.2.0 // indirect
- golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 // indirect
- golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect
- golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
- golang.org/x/text v0.3.7 // indirect
- gopkg.in/square/go-jose.v2 v2.6.0 // indirect
- gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+ github.com/tidwall/pretty v1.2.1 // indirect
+ go.uber.org/multierr v1.11.0 // indirect
+ golang.org/x/crypto v0.15.0 // indirect
+ golang.org/x/sys v0.14.0 // indirect
+ golang.org/x/term v0.14.0 // indirect
+ golang.org/x/text v0.14.0 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/go.sum b/go.sum
index be8e98b4b..35a454fad 100644
--- a/go.sum
+++ b/go.sum
@@ -1,16 +1,18 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM=
-github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU=
-github.com/AlecAivazis/survey/v2 v2.3.2 h1:TqTB+aDDCLYhf9/bD2TwSO8u8jDSmMUd2SUVO4gCnU8=
-github.com/AlecAivazis/survey/v2 v2.3.2/go.mod h1:TH2kPCDU3Kqq7pLbnCWwZXDBjnhZtmsCle5EiYDJ2fg=
+github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
+github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
+github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
+github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
+github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ=
+github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo=
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
-github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
+github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
+github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
-github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
+github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
+github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU=
@@ -22,12 +24,12 @@ github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEq
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0=
github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY=
-github.com/aws/aws-sdk-go v1.42.44 h1:vPlF4cUsdN5ETfvb7ewZFbFZyB6Rsfndt3kS2XqLXKo=
-github.com/aws/aws-sdk-go v1.42.44/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
+github.com/aws/aws-sdk-go v1.47.10 h1:cvufN7WkD1nlOgpRopsmxKQlFp5X1MfyAw4r7BBORQc=
+github.com/aws/aws-sdk-go v1.47.10/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/bearsh/hid v1.3.0 h1:GLNa8hvEzJxzQEEpheDUr2SivvH7iwTrJrDhFKutfX8=
github.com/bearsh/hid v1.3.0/go.mod h1:KbQByg8WfPr92v7aaKAHTtZUEVG7e2XRpcF8+TopQv8=
-github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
-github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
+github.com/beevik/etree v1.2.0 h1:l7WETslUG/T+xOPs47dtd6jov2Ii/8/OjCldk5fYfQw=
+github.com/beevik/etree v1.2.0/go.mod h1:aiPf89g/1k3AShMVAzriilpcE4R/Vuor90y83zVZWFc=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
@@ -38,9 +40,10 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/danieljoos/wincred v1.0.2/go.mod h1:SnuYRW9lp1oJrZX/dXJqr0cPK5gYXqx3EJbmjhLdK9U=
-github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
-github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
+github.com/creack/pty v1.1.17 h1:QeVUsEDNrLBW4tMgZHvxy18sKtr6VI492kBhUfhDJNI=
+github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
+github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE=
+github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -48,14 +51,18 @@ 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/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ=
-github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
+github.com/dvsekhvalnov/jose2go v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
+github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
+github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
+github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -67,20 +74,22 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
+github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
-github.com/h2non/filetype v1.1.1/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
+github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
+github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
+github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
-github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
+github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -90,20 +99,16 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
-github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
github.com/keybase/go-keychain v0.0.0-20211119201326-e02f34051621 h1:aMQ7pA4f06yOVXSulygyGvy4xA94fyzjUGs0iqQdMOI=
github.com/keybase/go-keychain v0.0.0-20211119201326-e02f34051621/go.mod h1:enrU/ug069Om7vWxuFE6nikLI2BZNwevMiGSo43Kt5w=
-github.com/keybase/go.dbus v0.0.0-20200324223359-a94be52c0b03/go.mod h1:a8clEhrrGV/d76/f9r2I41BwANMihfZYV9C223vaxqE=
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=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
-github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
@@ -122,13 +127,16 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxschmitt/playwright-go v0.1400.0 h1:HL8dbxcVEobE+pNjASeYGJJRmd4+9gyu/51XO7d3qF0=
-github.com/mxschmitt/playwright-go v0.1400.0/go.mod h1:kUvZFgMneRGknVLtC2DKQ42lhZiCmWzxgBdGwjC0vkw=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4=
+github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
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.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/playwright-community/playwright-go v0.3900.1 h1:8BkmDxVzLTp3USQ50EyXJSXcz0XDMwNP5y29lHIZ9Fc=
+github.com/playwright-community/playwright-go v0.3900.1/go.mod h1:mbNzMqt04IVRdhVfXWqmCxd81gCdL3BA5hj6/pVAIqM=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -141,12 +149,13 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
+github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -159,50 +168,62 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+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/tidwall/gjson v1.13.0 h1:3TFY9yxOQShrvmjdM76K+jc66zJeT6D3/VFFYCGQf7M=
-github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
+github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
-github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
+github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
+github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
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=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
+go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 h1:3zb4D3T4G8jdExgVU/95+vQXfpEPiMdCaZgmGVxjNHM=
-golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA=
+golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=
+golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
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=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -211,46 +232,55 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/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-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8=
+golang.org/x/term v0.14.0/go.mod h1:TySc+nGkYR6qt8km8wUhuFRTVSMIX3XPR58y2lC8vww=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
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-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-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w=
-gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-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.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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
diff --git a/helper/linuxkeyring/linuxkeyring_linux.go b/helper/linuxkeyring/linuxkeyring_linux.go
index 719b2e2be..123e7c787 100644
--- a/helper/linuxkeyring/linuxkeyring_linux.go
+++ b/helper/linuxkeyring/linuxkeyring_linux.go
@@ -14,8 +14,12 @@ type KeyringHelper struct {
keyring keyring.Keyring
}
-func NewKeyringHelper() (*KeyringHelper, error) {
- kr, err := keyring.Open(keyring.Config{
+type Configuration struct {
+ Backend string
+}
+
+func NewKeyringHelper(config Configuration) (*KeyringHelper, error) {
+ c := keyring.Config{
AllowedBackends: []keyring.BackendType{
keyring.KWalletBackend,
keyring.SecretServiceBackend,
@@ -23,7 +27,14 @@ func NewKeyringHelper() (*KeyringHelper, error) {
},
LibSecretCollectionName: "login",
PassPrefix: "saml2aws",
- })
+ }
+
+ // set the only allowed backend to be backend configured
+ if config.Backend != "" {
+ c.AllowedBackends = []keyring.BackendType{keyring.BackendType(config.Backend)}
+ }
+
+ kr, err := keyring.Open(c)
if err != nil {
return nil, err
diff --git a/helper/osxkeychain/keychain.go b/helper/osxkeychain/keychain.go
index 3a9ec5ba7..555655f02 100644
--- a/helper/osxkeychain/keychain.go
+++ b/helper/osxkeychain/keychain.go
@@ -1,3 +1,4 @@
+//go:build darwin && cgo
// +build darwin,cgo
package osxkeychain
diff --git a/helper/osxkeychain/osxkeychain.go b/helper/osxkeychain/osxkeychain.go
index cc0f951b0..93e2bcdf3 100644
--- a/helper/osxkeychain/osxkeychain.go
+++ b/helper/osxkeychain/osxkeychain.go
@@ -1,3 +1,4 @@
+//go:build darwin && cgo
// +build darwin,cgo
package osxkeychain
diff --git a/helper/osxkeychain/osxkeychain_test.go b/helper/osxkeychain/osxkeychain_test.go
index de9cda899..78b85337c 100644
--- a/helper/osxkeychain/osxkeychain_test.go
+++ b/helper/osxkeychain/osxkeychain_test.go
@@ -1,3 +1,4 @@
+//go:build darwin && cgo
// +build darwin,cgo
// Copyright (c) 2016 David Calavera
diff --git a/mocks/Page.go b/mocks/Page.go
new file mode 100644
index 000000000..0a2b7bdd5
--- /dev/null
+++ b/mocks/Page.go
@@ -0,0 +1,2484 @@
+// Code generated by mockery v2.36.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ playwright "github.com/playwright-community/playwright-go"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// Page is an autogenerated mock type for the Page type
+type Page struct {
+ mock.Mock
+}
+
+// AddInitScript provides a mock function with given fields: script
+func (_m *Page) AddInitScript(script playwright.Script) error {
+ ret := _m.Called(script)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(playwright.Script) error); ok {
+ r0 = rf(script)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// AddScriptTag provides a mock function with given fields: options
+func (_m *Page) AddScriptTag(options playwright.PageAddScriptTagOptions) (playwright.ElementHandle, error) {
+ ret := _m.Called(options)
+
+ var r0 playwright.ElementHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(playwright.PageAddScriptTagOptions) (playwright.ElementHandle, error)); ok {
+ return rf(options)
+ }
+ if rf, ok := ret.Get(0).(func(playwright.PageAddScriptTagOptions) playwright.ElementHandle); ok {
+ r0 = rf(options)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.ElementHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(playwright.PageAddScriptTagOptions) error); ok {
+ r1 = rf(options)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// AddStyleTag provides a mock function with given fields: options
+func (_m *Page) AddStyleTag(options playwright.PageAddStyleTagOptions) (playwright.ElementHandle, error) {
+ ret := _m.Called(options)
+
+ var r0 playwright.ElementHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(playwright.PageAddStyleTagOptions) (playwright.ElementHandle, error)); ok {
+ return rf(options)
+ }
+ if rf, ok := ret.Get(0).(func(playwright.PageAddStyleTagOptions) playwright.ElementHandle); ok {
+ r0 = rf(options)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.ElementHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(playwright.PageAddStyleTagOptions) error); ok {
+ r1 = rf(options)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// BringToFront provides a mock function with given fields:
+func (_m *Page) BringToFront() error {
+ ret := _m.Called()
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func() error); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Check provides a mock function with given fields: selector, options
+func (_m *Page) Check(selector string, options ...playwright.PageCheckOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageCheckOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Click provides a mock function with given fields: selector, options
+func (_m *Page) Click(selector string, options ...playwright.PageClickOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageClickOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Close provides a mock function with given fields: options
+func (_m *Page) Close(options ...playwright.PageCloseOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageCloseOptions) error); ok {
+ r0 = rf(options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Content provides a mock function with given fields:
+func (_m *Page) Content() (string, error) {
+ ret := _m.Called()
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Context provides a mock function with given fields:
+func (_m *Page) Context() playwright.BrowserContext {
+ ret := _m.Called()
+
+ var r0 playwright.BrowserContext
+ if rf, ok := ret.Get(0).(func() playwright.BrowserContext); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.BrowserContext)
+ }
+ }
+
+ return r0
+}
+
+// Dblclick provides a mock function with given fields: selector, options
+func (_m *Page) Dblclick(selector string, options ...playwright.PageDblclickOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageDblclickOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// DispatchEvent provides a mock function with given fields: selector, typ, eventInit, options
+func (_m *Page) DispatchEvent(selector string, typ string, eventInit interface{}, options ...playwright.PageDispatchEventOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, typ, eventInit)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, string, interface{}, ...playwright.PageDispatchEventOptions) error); ok {
+ r0 = rf(selector, typ, eventInit, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// DragAndDrop provides a mock function with given fields: source, target, options
+func (_m *Page) DragAndDrop(source string, target string, options ...playwright.PageDragAndDropOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, source, target)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PageDragAndDropOptions) error); ok {
+ r0 = rf(source, target, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Emit provides a mock function with given fields: name, payload
+func (_m *Page) Emit(name string, payload ...interface{}) bool {
+ var _ca []interface{}
+ _ca = append(_ca, name)
+ _ca = append(_ca, payload...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func(string, ...interface{}) bool); ok {
+ r0 = rf(name, payload...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// EmulateMedia provides a mock function with given fields: options
+func (_m *Page) EmulateMedia(options ...playwright.PageEmulateMediaOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageEmulateMediaOptions) error); ok {
+ r0 = rf(options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// EvalOnSelector provides a mock function with given fields: selector, expression, arg, options
+func (_m *Page) EvalOnSelector(selector string, expression string, arg interface{}, options ...playwright.PageEvalOnSelectorOptions) (interface{}, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, expression, arg)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, string, interface{}, ...playwright.PageEvalOnSelectorOptions) (interface{}, error)); ok {
+ return rf(selector, expression, arg, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, string, interface{}, ...playwright.PageEvalOnSelectorOptions) interface{}); ok {
+ r0 = rf(selector, expression, arg, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, string, interface{}, ...playwright.PageEvalOnSelectorOptions) error); ok {
+ r1 = rf(selector, expression, arg, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// EvalOnSelectorAll provides a mock function with given fields: selector, expression, arg
+func (_m *Page) EvalOnSelectorAll(selector string, expression string, arg ...interface{}) (interface{}, error) {
+ var _ca []interface{}
+ _ca = append(_ca, selector, expression)
+ _ca = append(_ca, arg...)
+ ret := _m.Called(_ca...)
+
+ var r0 interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, string, ...interface{}) (interface{}, error)); ok {
+ return rf(selector, expression, arg...)
+ }
+ if rf, ok := ret.Get(0).(func(string, string, ...interface{}) interface{}); ok {
+ r0 = rf(selector, expression, arg...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, string, ...interface{}) error); ok {
+ r1 = rf(selector, expression, arg...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Evaluate provides a mock function with given fields: expression, arg
+func (_m *Page) Evaluate(expression string, arg ...interface{}) (interface{}, error) {
+ var _ca []interface{}
+ _ca = append(_ca, expression)
+ _ca = append(_ca, arg...)
+ ret := _m.Called(_ca...)
+
+ var r0 interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...interface{}) (interface{}, error)); ok {
+ return rf(expression, arg...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...interface{}) interface{}); ok {
+ r0 = rf(expression, arg...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok {
+ r1 = rf(expression, arg...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// EvaluateHandle provides a mock function with given fields: expression, arg
+func (_m *Page) EvaluateHandle(expression string, arg ...interface{}) (playwright.JSHandle, error) {
+ var _ca []interface{}
+ _ca = append(_ca, expression)
+ _ca = append(_ca, arg...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.JSHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...interface{}) (playwright.JSHandle, error)); ok {
+ return rf(expression, arg...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...interface{}) playwright.JSHandle); ok {
+ r0 = rf(expression, arg...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.JSHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...interface{}) error); ok {
+ r1 = rf(expression, arg...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectConsoleMessage provides a mock function with given fields: cb, options
+func (_m *Page) ExpectConsoleMessage(cb func() error, options ...playwright.PageExpectConsoleMessageOptions) (playwright.ConsoleMessage, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.ConsoleMessage
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectConsoleMessageOptions) (playwright.ConsoleMessage, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectConsoleMessageOptions) playwright.ConsoleMessage); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.ConsoleMessage)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectConsoleMessageOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectDownload provides a mock function with given fields: cb, options
+func (_m *Page) ExpectDownload(cb func() error, options ...playwright.PageExpectDownloadOptions) (playwright.Download, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Download
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectDownloadOptions) (playwright.Download, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectDownloadOptions) playwright.Download); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Download)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectDownloadOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectEvent provides a mock function with given fields: event, cb, options
+func (_m *Page) ExpectEvent(event string, cb func() error, options ...playwright.PageExpectEventOptions) (interface{}, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, event, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, func() error, ...playwright.PageExpectEventOptions) (interface{}, error)); ok {
+ return rf(event, cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, func() error, ...playwright.PageExpectEventOptions) interface{}); ok {
+ r0 = rf(event, cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, func() error, ...playwright.PageExpectEventOptions) error); ok {
+ r1 = rf(event, cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectFileChooser provides a mock function with given fields: cb, options
+func (_m *Page) ExpectFileChooser(cb func() error, options ...playwright.PageExpectFileChooserOptions) (playwright.FileChooser, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.FileChooser
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectFileChooserOptions) (playwright.FileChooser, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectFileChooserOptions) playwright.FileChooser); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.FileChooser)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectFileChooserOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectNavigation provides a mock function with given fields: cb, options
+func (_m *Page) ExpectNavigation(cb func() error, options ...playwright.PageExpectNavigationOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectNavigationOptions) (playwright.Response, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectNavigationOptions) playwright.Response); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectNavigationOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectPopup provides a mock function with given fields: cb, options
+func (_m *Page) ExpectPopup(cb func() error, options ...playwright.PageExpectPopupOptions) (playwright.Page, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Page
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectPopupOptions) (playwright.Page, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectPopupOptions) playwright.Page); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Page)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectPopupOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectRequest provides a mock function with given fields: urlOrPredicate, cb, options
+func (_m *Page) ExpectRequest(urlOrPredicate interface{}, cb func() error, options ...playwright.PageExpectRequestOptions) (playwright.Request, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, urlOrPredicate, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Request
+ var r1 error
+ if rf, ok := ret.Get(0).(func(interface{}, func() error, ...playwright.PageExpectRequestOptions) (playwright.Request, error)); ok {
+ return rf(urlOrPredicate, cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(interface{}, func() error, ...playwright.PageExpectRequestOptions) playwright.Request); ok {
+ r0 = rf(urlOrPredicate, cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Request)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(interface{}, func() error, ...playwright.PageExpectRequestOptions) error); ok {
+ r1 = rf(urlOrPredicate, cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectRequestFinished provides a mock function with given fields: cb, options
+func (_m *Page) ExpectRequestFinished(cb func() error, options ...playwright.PageExpectRequestFinishedOptions) (playwright.Request, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Request
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectRequestFinishedOptions) (playwright.Request, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectRequestFinishedOptions) playwright.Request); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Request)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectRequestFinishedOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectResponse provides a mock function with given fields: urlOrPredicate, cb, options
+func (_m *Page) ExpectResponse(urlOrPredicate interface{}, cb func() error, options ...playwright.PageExpectResponseOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, urlOrPredicate, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(interface{}, func() error, ...playwright.PageExpectResponseOptions) (playwright.Response, error)); ok {
+ return rf(urlOrPredicate, cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(interface{}, func() error, ...playwright.PageExpectResponseOptions) playwright.Response); ok {
+ r0 = rf(urlOrPredicate, cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(interface{}, func() error, ...playwright.PageExpectResponseOptions) error); ok {
+ r1 = rf(urlOrPredicate, cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectWebSocket provides a mock function with given fields: cb, options
+func (_m *Page) ExpectWebSocket(cb func() error, options ...playwright.PageExpectWebSocketOptions) (playwright.WebSocket, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.WebSocket
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectWebSocketOptions) (playwright.WebSocket, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectWebSocketOptions) playwright.WebSocket); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.WebSocket)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectWebSocketOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExpectWorker provides a mock function with given fields: cb, options
+func (_m *Page) ExpectWorker(cb func() error, options ...playwright.PageExpectWorkerOptions) (playwright.Worker, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, cb)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Worker
+ var r1 error
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectWorkerOptions) (playwright.Worker, error)); ok {
+ return rf(cb, options...)
+ }
+ if rf, ok := ret.Get(0).(func(func() error, ...playwright.PageExpectWorkerOptions) playwright.Worker); ok {
+ r0 = rf(cb, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Worker)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(func() error, ...playwright.PageExpectWorkerOptions) error); ok {
+ r1 = rf(cb, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ExposeBinding provides a mock function with given fields: name, binding, handle
+func (_m *Page) ExposeBinding(name string, binding playwright.BindingCallFunction, handle ...bool) error {
+ _va := make([]interface{}, len(handle))
+ for _i := range handle {
+ _va[_i] = handle[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, name, binding)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, playwright.BindingCallFunction, ...bool) error); ok {
+ r0 = rf(name, binding, handle...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// ExposeFunction provides a mock function with given fields: name, binding
+func (_m *Page) ExposeFunction(name string, binding func([]interface{}) interface{}) error {
+ ret := _m.Called(name, binding)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, func([]interface{}) interface{}) error); ok {
+ r0 = rf(name, binding)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Fill provides a mock function with given fields: selector, value, options
+func (_m *Page) Fill(selector string, value string, options ...playwright.PageFillOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, value)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PageFillOptions) error); ok {
+ r0 = rf(selector, value, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Focus provides a mock function with given fields: selector, options
+func (_m *Page) Focus(selector string, options ...playwright.PageFocusOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageFocusOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Frame provides a mock function with given fields: options
+func (_m *Page) Frame(options ...playwright.PageFrameOptions) playwright.Frame {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Frame
+ if rf, ok := ret.Get(0).(func(...playwright.PageFrameOptions) playwright.Frame); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Frame)
+ }
+ }
+
+ return r0
+}
+
+// FrameLocator provides a mock function with given fields: selector
+func (_m *Page) FrameLocator(selector string) playwright.FrameLocator {
+ ret := _m.Called(selector)
+
+ var r0 playwright.FrameLocator
+ if rf, ok := ret.Get(0).(func(string) playwright.FrameLocator); ok {
+ r0 = rf(selector)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.FrameLocator)
+ }
+ }
+
+ return r0
+}
+
+// Frames provides a mock function with given fields:
+func (_m *Page) Frames() []playwright.Frame {
+ ret := _m.Called()
+
+ var r0 []playwright.Frame
+ if rf, ok := ret.Get(0).(func() []playwright.Frame); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]playwright.Frame)
+ }
+ }
+
+ return r0
+}
+
+// GetAttribute provides a mock function with given fields: selector, name, options
+func (_m *Page) GetAttribute(selector string, name string, options ...playwright.PageGetAttributeOptions) (string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, name)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PageGetAttributeOptions) (string, error)); ok {
+ return rf(selector, name, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PageGetAttributeOptions) string); ok {
+ r0 = rf(selector, name, options...)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, string, ...playwright.PageGetAttributeOptions) error); ok {
+ r1 = rf(selector, name, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// GetByAltText provides a mock function with given fields: text, options
+func (_m *Page) GetByAltText(text interface{}, options ...playwright.PageGetByAltTextOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageGetByAltTextOptions) playwright.Locator); ok {
+ r0 = rf(text, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByLabel provides a mock function with given fields: text, options
+func (_m *Page) GetByLabel(text interface{}, options ...playwright.PageGetByLabelOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageGetByLabelOptions) playwright.Locator); ok {
+ r0 = rf(text, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByPlaceholder provides a mock function with given fields: text, options
+func (_m *Page) GetByPlaceholder(text interface{}, options ...playwright.PageGetByPlaceholderOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageGetByPlaceholderOptions) playwright.Locator); ok {
+ r0 = rf(text, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByRole provides a mock function with given fields: role, options
+func (_m *Page) GetByRole(role playwright.AriaRole, options ...playwright.PageGetByRoleOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, role)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(playwright.AriaRole, ...playwright.PageGetByRoleOptions) playwright.Locator); ok {
+ r0 = rf(role, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByTestId provides a mock function with given fields: testId
+func (_m *Page) GetByTestId(testId interface{}) playwright.Locator {
+ ret := _m.Called(testId)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}) playwright.Locator); ok {
+ r0 = rf(testId)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByText provides a mock function with given fields: text, options
+func (_m *Page) GetByText(text interface{}, options ...playwright.PageGetByTextOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageGetByTextOptions) playwright.Locator); ok {
+ r0 = rf(text, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GetByTitle provides a mock function with given fields: text, options
+func (_m *Page) GetByTitle(text interface{}, options ...playwright.PageGetByTitleOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageGetByTitleOptions) playwright.Locator); ok {
+ r0 = rf(text, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// GoBack provides a mock function with given fields: options
+func (_m *Page) GoBack(options ...playwright.PageGoBackOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageGoBackOptions) (playwright.Response, error)); ok {
+ return rf(options...)
+ }
+ if rf, ok := ret.Get(0).(func(...playwright.PageGoBackOptions) playwright.Response); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(...playwright.PageGoBackOptions) error); ok {
+ r1 = rf(options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// GoForward provides a mock function with given fields: options
+func (_m *Page) GoForward(options ...playwright.PageGoForwardOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageGoForwardOptions) (playwright.Response, error)); ok {
+ return rf(options...)
+ }
+ if rf, ok := ret.Get(0).(func(...playwright.PageGoForwardOptions) playwright.Response); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(...playwright.PageGoForwardOptions) error); ok {
+ r1 = rf(options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Goto provides a mock function with given fields: url, options
+func (_m *Page) Goto(url string, options ...playwright.PageGotoOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, url)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageGotoOptions) (playwright.Response, error)); ok {
+ return rf(url, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageGotoOptions) playwright.Response); ok {
+ r0 = rf(url, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageGotoOptions) error); ok {
+ r1 = rf(url, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Hover provides a mock function with given fields: selector, options
+func (_m *Page) Hover(selector string, options ...playwright.PageHoverOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageHoverOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// InnerHTML provides a mock function with given fields: selector, options
+func (_m *Page) InnerHTML(selector string, options ...playwright.PageInnerHTMLOptions) (string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInnerHTMLOptions) (string, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInnerHTMLOptions) string); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageInnerHTMLOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// InnerText provides a mock function with given fields: selector, options
+func (_m *Page) InnerText(selector string, options ...playwright.PageInnerTextOptions) (string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInnerTextOptions) (string, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInnerTextOptions) string); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageInnerTextOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// InputValue provides a mock function with given fields: selector, options
+func (_m *Page) InputValue(selector string, options ...playwright.PageInputValueOptions) (string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInputValueOptions) (string, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageInputValueOptions) string); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageInputValueOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsChecked provides a mock function with given fields: selector, options
+func (_m *Page) IsChecked(selector string, options ...playwright.PageIsCheckedOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsCheckedOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsCheckedOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsCheckedOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsClosed provides a mock function with given fields:
+func (_m *Page) IsClosed() bool {
+ ret := _m.Called()
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func() bool); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// IsDisabled provides a mock function with given fields: selector, options
+func (_m *Page) IsDisabled(selector string, options ...playwright.PageIsDisabledOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsDisabledOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsDisabledOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsDisabledOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsEditable provides a mock function with given fields: selector, options
+func (_m *Page) IsEditable(selector string, options ...playwright.PageIsEditableOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsEditableOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsEditableOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsEditableOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsEnabled provides a mock function with given fields: selector, options
+func (_m *Page) IsEnabled(selector string, options ...playwright.PageIsEnabledOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsEnabledOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsEnabledOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsEnabledOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsHidden provides a mock function with given fields: selector, options
+func (_m *Page) IsHidden(selector string, options ...playwright.PageIsHiddenOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsHiddenOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsHiddenOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsHiddenOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsVisible provides a mock function with given fields: selector, options
+func (_m *Page) IsVisible(selector string, options ...playwright.PageIsVisibleOptions) (bool, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 bool
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsVisibleOptions) (bool, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageIsVisibleOptions) bool); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageIsVisibleOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Keyboard provides a mock function with given fields:
+func (_m *Page) Keyboard() playwright.Keyboard {
+ ret := _m.Called()
+
+ var r0 playwright.Keyboard
+ if rf, ok := ret.Get(0).(func() playwright.Keyboard); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Keyboard)
+ }
+ }
+
+ return r0
+}
+
+// ListenerCount provides a mock function with given fields: name
+func (_m *Page) ListenerCount(name string) int {
+ ret := _m.Called(name)
+
+ var r0 int
+ if rf, ok := ret.Get(0).(func(string) int); ok {
+ r0 = rf(name)
+ } else {
+ r0 = ret.Get(0).(int)
+ }
+
+ return r0
+}
+
+// Locator provides a mock function with given fields: selector, options
+func (_m *Page) Locator(selector string, options ...playwright.PageLocatorOptions) playwright.Locator {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Locator
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageLocatorOptions) playwright.Locator); ok {
+ r0 = rf(selector, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Locator)
+ }
+ }
+
+ return r0
+}
+
+// MainFrame provides a mock function with given fields:
+func (_m *Page) MainFrame() playwright.Frame {
+ ret := _m.Called()
+
+ var r0 playwright.Frame
+ if rf, ok := ret.Get(0).(func() playwright.Frame); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Frame)
+ }
+ }
+
+ return r0
+}
+
+// Mouse provides a mock function with given fields:
+func (_m *Page) Mouse() playwright.Mouse {
+ ret := _m.Called()
+
+ var r0 playwright.Mouse
+ if rf, ok := ret.Get(0).(func() playwright.Mouse); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Mouse)
+ }
+ }
+
+ return r0
+}
+
+// On provides a mock function with given fields: name, handler
+func (_m *Page) On(name string, handler interface{}) {
+ _m.Called(name, handler)
+}
+
+// OnClose provides a mock function with given fields: fn
+func (_m *Page) OnClose(fn func(playwright.Page)) {
+ _m.Called(fn)
+}
+
+// OnConsole provides a mock function with given fields: fn
+func (_m *Page) OnConsole(fn func(playwright.ConsoleMessage)) {
+ _m.Called(fn)
+}
+
+// OnCrash provides a mock function with given fields: fn
+func (_m *Page) OnCrash(fn func(playwright.Page)) {
+ _m.Called(fn)
+}
+
+// OnDOMContentLoaded provides a mock function with given fields: fn
+func (_m *Page) OnDOMContentLoaded(fn func(playwright.Page)) {
+ _m.Called(fn)
+}
+
+// OnDialog provides a mock function with given fields: fn
+func (_m *Page) OnDialog(fn func(playwright.Dialog)) {
+ _m.Called(fn)
+}
+
+// OnDownload provides a mock function with given fields: fn
+func (_m *Page) OnDownload(fn func(playwright.Download)) {
+ _m.Called(fn)
+}
+
+// OnFileChooser provides a mock function with given fields: fn
+func (_m *Page) OnFileChooser(fn func(playwright.FileChooser)) {
+ _m.Called(fn)
+}
+
+// OnFrameAttached provides a mock function with given fields: fn
+func (_m *Page) OnFrameAttached(fn func(playwright.Frame)) {
+ _m.Called(fn)
+}
+
+// OnFrameDetached provides a mock function with given fields: fn
+func (_m *Page) OnFrameDetached(fn func(playwright.Frame)) {
+ _m.Called(fn)
+}
+
+// OnFrameNavigated provides a mock function with given fields: fn
+func (_m *Page) OnFrameNavigated(fn func(playwright.Frame)) {
+ _m.Called(fn)
+}
+
+// OnLoad provides a mock function with given fields: fn
+func (_m *Page) OnLoad(fn func(playwright.Page)) {
+ _m.Called(fn)
+}
+
+// OnPageError provides a mock function with given fields: fn
+func (_m *Page) OnPageError(fn func(*playwright.Error)) {
+ _m.Called(fn)
+}
+
+// OnPopup provides a mock function with given fields: fn
+func (_m *Page) OnPopup(fn func(playwright.Page)) {
+ _m.Called(fn)
+}
+
+// OnRequest provides a mock function with given fields: fn
+func (_m *Page) OnRequest(fn func(playwright.Request)) {
+ _m.Called(fn)
+}
+
+// OnRequestFailed provides a mock function with given fields: fn
+func (_m *Page) OnRequestFailed(fn func(playwright.Request)) {
+ _m.Called(fn)
+}
+
+// OnRequestFinished provides a mock function with given fields: fn
+func (_m *Page) OnRequestFinished(fn func(playwright.Request)) {
+ _m.Called(fn)
+}
+
+// OnResponse provides a mock function with given fields: fn
+func (_m *Page) OnResponse(fn func(playwright.Response)) {
+ _m.Called(fn)
+}
+
+// OnWebSocket provides a mock function with given fields: fn
+func (_m *Page) OnWebSocket(fn func(playwright.WebSocket)) {
+ _m.Called(fn)
+}
+
+// OnWorker provides a mock function with given fields: fn
+func (_m *Page) OnWorker(fn func(playwright.Worker)) {
+ _m.Called(fn)
+}
+
+// Once provides a mock function with given fields: name, handler
+func (_m *Page) Once(name string, handler interface{}) {
+ _m.Called(name, handler)
+}
+
+// Opener provides a mock function with given fields:
+func (_m *Page) Opener() (playwright.Page, error) {
+ ret := _m.Called()
+
+ var r0 playwright.Page
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (playwright.Page, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() playwright.Page); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Page)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// PDF provides a mock function with given fields: options
+func (_m *Page) PDF(options ...playwright.PagePdfOptions) ([]byte, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func(...playwright.PagePdfOptions) ([]byte, error)); ok {
+ return rf(options...)
+ }
+ if rf, ok := ret.Get(0).(func(...playwright.PagePdfOptions) []byte); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(...playwright.PagePdfOptions) error); ok {
+ r1 = rf(options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Pause provides a mock function with given fields:
+func (_m *Page) Pause() error {
+ ret := _m.Called()
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func() error); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Press provides a mock function with given fields: selector, key, options
+func (_m *Page) Press(selector string, key string, options ...playwright.PagePressOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, key)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PagePressOptions) error); ok {
+ r0 = rf(selector, key, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// QuerySelector provides a mock function with given fields: selector, options
+func (_m *Page) QuerySelector(selector string, options ...playwright.PageQuerySelectorOptions) (playwright.ElementHandle, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.ElementHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageQuerySelectorOptions) (playwright.ElementHandle, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageQuerySelectorOptions) playwright.ElementHandle); ok {
+ r0 = rf(selector, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.ElementHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageQuerySelectorOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// QuerySelectorAll provides a mock function with given fields: selector
+func (_m *Page) QuerySelectorAll(selector string) ([]playwright.ElementHandle, error) {
+ ret := _m.Called(selector)
+
+ var r0 []playwright.ElementHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) ([]playwright.ElementHandle, error)); ok {
+ return rf(selector)
+ }
+ if rf, ok := ret.Get(0).(func(string) []playwright.ElementHandle); ok {
+ r0 = rf(selector)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]playwright.ElementHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(selector)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Reload provides a mock function with given fields: options
+func (_m *Page) Reload(options ...playwright.PageReloadOptions) (playwright.Response, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageReloadOptions) (playwright.Response, error)); ok {
+ return rf(options...)
+ }
+ if rf, ok := ret.Get(0).(func(...playwright.PageReloadOptions) playwright.Response); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(...playwright.PageReloadOptions) error); ok {
+ r1 = rf(options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// RemoveListener provides a mock function with given fields: name, handler
+func (_m *Page) RemoveListener(name string, handler interface{}) {
+ _m.Called(name, handler)
+}
+
+// Request provides a mock function with given fields:
+func (_m *Page) Request() playwright.APIRequestContext {
+ ret := _m.Called()
+
+ var r0 playwright.APIRequestContext
+ if rf, ok := ret.Get(0).(func() playwright.APIRequestContext); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.APIRequestContext)
+ }
+ }
+
+ return r0
+}
+
+// Route provides a mock function with given fields: url, handler, times
+func (_m *Page) Route(url interface{}, handler func(playwright.Route), times ...int) error {
+ _va := make([]interface{}, len(times))
+ for _i := range times {
+ _va[_i] = times[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, url, handler)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(interface{}, func(playwright.Route), ...int) error); ok {
+ r0 = rf(url, handler, times...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// RouteFromHAR provides a mock function with given fields: har, options
+func (_m *Page) RouteFromHAR(har string, options ...playwright.PageRouteFromHAROptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, har)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageRouteFromHAROptions) error); ok {
+ r0 = rf(har, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Screenshot provides a mock function with given fields: options
+func (_m *Page) Screenshot(options ...playwright.PageScreenshotOptions) ([]byte, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageScreenshotOptions) ([]byte, error)); ok {
+ return rf(options...)
+ }
+ if rf, ok := ret.Get(0).(func(...playwright.PageScreenshotOptions) []byte); ok {
+ r0 = rf(options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(...playwright.PageScreenshotOptions) error); ok {
+ r1 = rf(options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// SelectOption provides a mock function with given fields: selector, values, options
+func (_m *Page) SelectOption(selector string, values playwright.SelectOptionValues, options ...playwright.PageSelectOptionOptions) ([]string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, values)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 []string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, playwright.SelectOptionValues, ...playwright.PageSelectOptionOptions) ([]string, error)); ok {
+ return rf(selector, values, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, playwright.SelectOptionValues, ...playwright.PageSelectOptionOptions) []string); ok {
+ r0 = rf(selector, values, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]string)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, playwright.SelectOptionValues, ...playwright.PageSelectOptionOptions) error); ok {
+ r1 = rf(selector, values, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// SetChecked provides a mock function with given fields: selector, checked, options
+func (_m *Page) SetChecked(selector string, checked bool, options ...playwright.PageSetCheckedOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, checked)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, bool, ...playwright.PageSetCheckedOptions) error); ok {
+ r0 = rf(selector, checked, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// SetContent provides a mock function with given fields: html, options
+func (_m *Page) SetContent(html string, options ...playwright.PageSetContentOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, html)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageSetContentOptions) error); ok {
+ r0 = rf(html, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// SetDefaultNavigationTimeout provides a mock function with given fields: timeout
+func (_m *Page) SetDefaultNavigationTimeout(timeout float64) {
+ _m.Called(timeout)
+}
+
+// SetDefaultTimeout provides a mock function with given fields: timeout
+func (_m *Page) SetDefaultTimeout(timeout float64) {
+ _m.Called(timeout)
+}
+
+// SetExtraHTTPHeaders provides a mock function with given fields: headers
+func (_m *Page) SetExtraHTTPHeaders(headers map[string]string) error {
+ ret := _m.Called(headers)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(map[string]string) error); ok {
+ r0 = rf(headers)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// SetInputFiles provides a mock function with given fields: selector, files, options
+func (_m *Page) SetInputFiles(selector string, files []playwright.InputFile, options ...playwright.PageSetInputFilesOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, files)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, []playwright.InputFile, ...playwright.PageSetInputFilesOptions) error); ok {
+ r0 = rf(selector, files, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// SetViewportSize provides a mock function with given fields: width, height
+func (_m *Page) SetViewportSize(width int, height int) error {
+ ret := _m.Called(width, height)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(int, int) error); ok {
+ r0 = rf(width, height)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Tap provides a mock function with given fields: selector, options
+func (_m *Page) Tap(selector string, options ...playwright.PageTapOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageTapOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// TextContent provides a mock function with given fields: selector, options
+func (_m *Page) TextContent(selector string, options ...playwright.PageTextContentOptions) (string, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageTextContentOptions) (string, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageTextContentOptions) string); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageTextContentOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Title provides a mock function with given fields:
+func (_m *Page) Title() (string, error) {
+ ret := _m.Called()
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Touchscreen provides a mock function with given fields:
+func (_m *Page) Touchscreen() playwright.Touchscreen {
+ ret := _m.Called()
+
+ var r0 playwright.Touchscreen
+ if rf, ok := ret.Get(0).(func() playwright.Touchscreen); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Touchscreen)
+ }
+ }
+
+ return r0
+}
+
+// Type provides a mock function with given fields: selector, text, options
+func (_m *Page) Type(selector string, text string, options ...playwright.PageTypeOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector, text)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, string, ...playwright.PageTypeOptions) error); ok {
+ r0 = rf(selector, text, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// URL provides a mock function with given fields:
+func (_m *Page) URL() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// Uncheck provides a mock function with given fields: selector, options
+func (_m *Page) Uncheck(selector string, options ...playwright.PageUncheckOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageUncheckOptions) error); ok {
+ r0 = rf(selector, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Unroute provides a mock function with given fields: url, handler
+func (_m *Page) Unroute(url interface{}, handler ...func(playwright.Route)) error {
+ _va := make([]interface{}, len(handler))
+ for _i := range handler {
+ _va[_i] = handler[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, url)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(interface{}, ...func(playwright.Route)) error); ok {
+ r0 = rf(url, handler...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Video provides a mock function with given fields:
+func (_m *Page) Video() playwright.Video {
+ ret := _m.Called()
+
+ var r0 playwright.Video
+ if rf, ok := ret.Get(0).(func() playwright.Video); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Video)
+ }
+ }
+
+ return r0
+}
+
+// ViewportSize provides a mock function with given fields:
+func (_m *Page) ViewportSize() *playwright.Size {
+ ret := _m.Called()
+
+ var r0 *playwright.Size
+ if rf, ok := ret.Get(0).(func() *playwright.Size); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*playwright.Size)
+ }
+ }
+
+ return r0
+}
+
+// WaitForEvent provides a mock function with given fields: event, options
+func (_m *Page) WaitForEvent(event string, options ...playwright.PageWaitForEventOptions) (interface{}, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, event)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 interface{}
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageWaitForEventOptions) (interface{}, error)); ok {
+ return rf(event, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageWaitForEventOptions) interface{}); ok {
+ r0 = rf(event, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(interface{})
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageWaitForEventOptions) error); ok {
+ r1 = rf(event, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// WaitForFunction provides a mock function with given fields: expression, arg, options
+func (_m *Page) WaitForFunction(expression string, arg interface{}, options ...playwright.PageWaitForFunctionOptions) (playwright.JSHandle, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, expression, arg)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.JSHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, interface{}, ...playwright.PageWaitForFunctionOptions) (playwright.JSHandle, error)); ok {
+ return rf(expression, arg, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, interface{}, ...playwright.PageWaitForFunctionOptions) playwright.JSHandle); ok {
+ r0 = rf(expression, arg, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.JSHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, interface{}, ...playwright.PageWaitForFunctionOptions) error); ok {
+ r1 = rf(expression, arg, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// WaitForLoadState provides a mock function with given fields: options
+func (_m *Page) WaitForLoadState(options ...playwright.PageWaitForLoadStateOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(...playwright.PageWaitForLoadStateOptions) error); ok {
+ r0 = rf(options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// WaitForSelector provides a mock function with given fields: selector, options
+func (_m *Page) WaitForSelector(selector string, options ...playwright.PageWaitForSelectorOptions) (playwright.ElementHandle, error) {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, selector)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 playwright.ElementHandle
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageWaitForSelectorOptions) (playwright.ElementHandle, error)); ok {
+ return rf(selector, options...)
+ }
+ if rf, ok := ret.Get(0).(func(string, ...playwright.PageWaitForSelectorOptions) playwright.ElementHandle); ok {
+ r0 = rf(selector, options...)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.ElementHandle)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string, ...playwright.PageWaitForSelectorOptions) error); ok {
+ r1 = rf(selector, options...)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// WaitForTimeout provides a mock function with given fields: timeout
+func (_m *Page) WaitForTimeout(timeout float64) {
+ _m.Called(timeout)
+}
+
+// WaitForURL provides a mock function with given fields: url, options
+func (_m *Page) WaitForURL(url interface{}, options ...playwright.PageWaitForURLOptions) error {
+ _va := make([]interface{}, len(options))
+ for _i := range options {
+ _va[_i] = options[_i]
+ }
+ var _ca []interface{}
+ _ca = append(_ca, url)
+ _ca = append(_ca, _va...)
+ ret := _m.Called(_ca...)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(interface{}, ...playwright.PageWaitForURLOptions) error); ok {
+ r0 = rf(url, options...)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Workers provides a mock function with given fields:
+func (_m *Page) Workers() []playwright.Worker {
+ ret := _m.Called()
+
+ var r0 []playwright.Worker
+ if rf, ok := ret.Get(0).(func() []playwright.Worker); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]playwright.Worker)
+ }
+ }
+
+ return r0
+}
+
+// NewPage creates a new instance of Page. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewPage(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *Page {
+ mock := &Page{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/mocks/Prompter.go b/mocks/Prompter.go
index e1453dd8d..ff4baf1a2 100644
--- a/mocks/Prompter.go
+++ b/mocks/Prompter.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v1.0.0. DO NOT EDIT.
+// Code generated by mockery v2.36.0. DO NOT EDIT.
package mocks
@@ -28,13 +28,16 @@ func (_m *Prompter) ChooseWithDefault(_a0 string, _a1 string, _a2 []string) (str
ret := _m.Called(_a0, _a1, _a2)
var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string, string, []string) (string, error)); ok {
+ return rf(_a0, _a1, _a2)
+ }
if rf, ok := ret.Get(0).(func(string, string, []string) string); ok {
r0 = rf(_a0, _a1, _a2)
} else {
r0 = ret.Get(0).(string)
}
- var r1 error
if rf, ok := ret.Get(1).(func(string, string, []string) error); ok {
r1 = rf(_a0, _a1, _a2)
} else {
@@ -44,6 +47,11 @@ func (_m *Prompter) ChooseWithDefault(_a0 string, _a1 string, _a2 []string) (str
return r0, r1
}
+// Display provides a mock function with given fields: _a0
+func (_m *Prompter) Display(_a0 string) {
+ _m.Called(_a0)
+}
+
// Password provides a mock function with given fields: _a0
func (_m *Prompter) Password(_a0 string) string {
ret := _m.Called(_a0)
@@ -99,3 +107,17 @@ func (_m *Prompter) StringRequired(_a0 string) string {
return r0
}
+
+// NewPrompter creates a new instance of Prompter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewPrompter(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *Prompter {
+ mock := &Prompter{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/mocks/Request.go b/mocks/Request.go
new file mode 100644
index 000000000..69e312a6b
--- /dev/null
+++ b/mocks/Request.go
@@ -0,0 +1,369 @@
+// Code generated by mockery v2.36.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ playwright "github.com/playwright-community/playwright-go"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// Request is an autogenerated mock type for the Request type
+type Request struct {
+ mock.Mock
+}
+
+// AllHeaders provides a mock function with given fields:
+func (_m *Request) AllHeaders() (map[string]string, error) {
+ ret := _m.Called()
+
+ var r0 map[string]string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (map[string]string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() map[string]string); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(map[string]string)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Failure provides a mock function with given fields:
+func (_m *Request) Failure() error {
+ ret := _m.Called()
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func() error); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Frame provides a mock function with given fields:
+func (_m *Request) Frame() playwright.Frame {
+ ret := _m.Called()
+
+ var r0 playwright.Frame
+ if rf, ok := ret.Get(0).(func() playwright.Frame); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Frame)
+ }
+ }
+
+ return r0
+}
+
+// HeaderValue provides a mock function with given fields: name
+func (_m *Request) HeaderValue(name string) (string, error) {
+ ret := _m.Called(name)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
+ return rf(name)
+ }
+ if rf, ok := ret.Get(0).(func(string) string); ok {
+ r0 = rf(name)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(name)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Headers provides a mock function with given fields:
+func (_m *Request) Headers() map[string]string {
+ ret := _m.Called()
+
+ var r0 map[string]string
+ if rf, ok := ret.Get(0).(func() map[string]string); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(map[string]string)
+ }
+ }
+
+ return r0
+}
+
+// HeadersArray provides a mock function with given fields:
+func (_m *Request) HeadersArray() ([]playwright.NameValue, error) {
+ ret := _m.Called()
+
+ var r0 []playwright.NameValue
+ var r1 error
+ if rf, ok := ret.Get(0).(func() ([]playwright.NameValue, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() []playwright.NameValue); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]playwright.NameValue)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// IsNavigationRequest provides a mock function with given fields:
+func (_m *Request) IsNavigationRequest() bool {
+ ret := _m.Called()
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func() bool); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// Method provides a mock function with given fields:
+func (_m *Request) Method() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// PostData provides a mock function with given fields:
+func (_m *Request) PostData() (string, error) {
+ ret := _m.Called()
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// PostDataBuffer provides a mock function with given fields:
+func (_m *Request) PostDataBuffer() ([]byte, error) {
+ ret := _m.Called()
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func() ([]byte, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() []byte); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// PostDataJSON provides a mock function with given fields: v
+func (_m *Request) PostDataJSON(v interface{}) error {
+ ret := _m.Called(v)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(interface{}) error); ok {
+ r0 = rf(v)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// RedirectedFrom provides a mock function with given fields:
+func (_m *Request) RedirectedFrom() playwright.Request {
+ ret := _m.Called()
+
+ var r0 playwright.Request
+ if rf, ok := ret.Get(0).(func() playwright.Request); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Request)
+ }
+ }
+
+ return r0
+}
+
+// RedirectedTo provides a mock function with given fields:
+func (_m *Request) RedirectedTo() playwright.Request {
+ ret := _m.Called()
+
+ var r0 playwright.Request
+ if rf, ok := ret.Get(0).(func() playwright.Request); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Request)
+ }
+ }
+
+ return r0
+}
+
+// ResourceType provides a mock function with given fields:
+func (_m *Request) ResourceType() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// Response provides a mock function with given fields:
+func (_m *Request) Response() (playwright.Response, error) {
+ ret := _m.Called()
+
+ var r0 playwright.Response
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (playwright.Response, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() playwright.Response); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Response)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Sizes provides a mock function with given fields:
+func (_m *Request) Sizes() (*playwright.RequestSizesResult, error) {
+ ret := _m.Called()
+
+ var r0 *playwright.RequestSizesResult
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (*playwright.RequestSizesResult, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() *playwright.RequestSizesResult); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*playwright.RequestSizesResult)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Timing provides a mock function with given fields:
+func (_m *Request) Timing() *playwright.RequestTiming {
+ ret := _m.Called()
+
+ var r0 *playwright.RequestTiming
+ if rf, ok := ret.Get(0).(func() *playwright.RequestTiming); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*playwright.RequestTiming)
+ }
+ }
+
+ return r0
+}
+
+// URL provides a mock function with given fields:
+func (_m *Request) URL() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// NewRequest creates a new instance of Request. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewRequest(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *Request {
+ mock := &Request{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/mocks/Response.go b/mocks/Response.go
new file mode 100644
index 000000000..583aa83d6
--- /dev/null
+++ b/mocks/Response.go
@@ -0,0 +1,377 @@
+// Code generated by mockery v2.36.0. DO NOT EDIT.
+
+package mocks
+
+import (
+ playwright "github.com/playwright-community/playwright-go"
+ mock "github.com/stretchr/testify/mock"
+)
+
+// Response is an autogenerated mock type for the Response type
+type Response struct {
+ mock.Mock
+}
+
+// AllHeaders provides a mock function with given fields:
+func (_m *Response) AllHeaders() (map[string]string, error) {
+ ret := _m.Called()
+
+ var r0 map[string]string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (map[string]string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() map[string]string); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(map[string]string)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Body provides a mock function with given fields:
+func (_m *Response) Body() ([]byte, error) {
+ ret := _m.Called()
+
+ var r0 []byte
+ var r1 error
+ if rf, ok := ret.Get(0).(func() ([]byte, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() []byte); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]byte)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Finished provides a mock function with given fields:
+func (_m *Response) Finished() error {
+ ret := _m.Called()
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func() error); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Frame provides a mock function with given fields:
+func (_m *Response) Frame() playwright.Frame {
+ ret := _m.Called()
+
+ var r0 playwright.Frame
+ if rf, ok := ret.Get(0).(func() playwright.Frame); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Frame)
+ }
+ }
+
+ return r0
+}
+
+// FromServiceWorker provides a mock function with given fields:
+func (_m *Response) FromServiceWorker() bool {
+ ret := _m.Called()
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func() bool); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// HeaderValue provides a mock function with given fields: name
+func (_m *Response) HeaderValue(name string) (string, error) {
+ ret := _m.Called(name)
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
+ return rf(name)
+ }
+ if rf, ok := ret.Get(0).(func(string) string); ok {
+ r0 = rf(name)
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(name)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// HeaderValues provides a mock function with given fields: name
+func (_m *Response) HeaderValues(name string) ([]string, error) {
+ ret := _m.Called(name)
+
+ var r0 []string
+ var r1 error
+ if rf, ok := ret.Get(0).(func(string) ([]string, error)); ok {
+ return rf(name)
+ }
+ if rf, ok := ret.Get(0).(func(string) []string); ok {
+ r0 = rf(name)
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]string)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func(string) error); ok {
+ r1 = rf(name)
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Headers provides a mock function with given fields:
+func (_m *Response) Headers() map[string]string {
+ ret := _m.Called()
+
+ var r0 map[string]string
+ if rf, ok := ret.Get(0).(func() map[string]string); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(map[string]string)
+ }
+ }
+
+ return r0
+}
+
+// HeadersArray provides a mock function with given fields:
+func (_m *Response) HeadersArray() ([]playwright.NameValue, error) {
+ ret := _m.Called()
+
+ var r0 []playwright.NameValue
+ var r1 error
+ if rf, ok := ret.Get(0).(func() ([]playwright.NameValue, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() []playwright.NameValue); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).([]playwright.NameValue)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// JSON provides a mock function with given fields: v
+func (_m *Response) JSON(v interface{}) error {
+ ret := _m.Called(v)
+
+ var r0 error
+ if rf, ok := ret.Get(0).(func(interface{}) error); ok {
+ r0 = rf(v)
+ } else {
+ r0 = ret.Error(0)
+ }
+
+ return r0
+}
+
+// Ok provides a mock function with given fields:
+func (_m *Response) Ok() bool {
+ ret := _m.Called()
+
+ var r0 bool
+ if rf, ok := ret.Get(0).(func() bool); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(bool)
+ }
+
+ return r0
+}
+
+// Request provides a mock function with given fields:
+func (_m *Response) Request() playwright.Request {
+ ret := _m.Called()
+
+ var r0 playwright.Request
+ if rf, ok := ret.Get(0).(func() playwright.Request); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(playwright.Request)
+ }
+ }
+
+ return r0
+}
+
+// SecurityDetails provides a mock function with given fields:
+func (_m *Response) SecurityDetails() (*playwright.ResponseSecurityDetailsResult, error) {
+ ret := _m.Called()
+
+ var r0 *playwright.ResponseSecurityDetailsResult
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (*playwright.ResponseSecurityDetailsResult, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() *playwright.ResponseSecurityDetailsResult); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*playwright.ResponseSecurityDetailsResult)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// ServerAddr provides a mock function with given fields:
+func (_m *Response) ServerAddr() (*playwright.ResponseServerAddrResult, error) {
+ ret := _m.Called()
+
+ var r0 *playwright.ResponseServerAddrResult
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (*playwright.ResponseServerAddrResult, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() *playwright.ResponseServerAddrResult); ok {
+ r0 = rf()
+ } else {
+ if ret.Get(0) != nil {
+ r0 = ret.Get(0).(*playwright.ResponseServerAddrResult)
+ }
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// Status provides a mock function with given fields:
+func (_m *Response) Status() int {
+ ret := _m.Called()
+
+ var r0 int
+ if rf, ok := ret.Get(0).(func() int); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(int)
+ }
+
+ return r0
+}
+
+// StatusText provides a mock function with given fields:
+func (_m *Response) StatusText() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// Text provides a mock function with given fields:
+func (_m *Response) Text() (string, error) {
+ ret := _m.Called()
+
+ var r0 string
+ var r1 error
+ if rf, ok := ret.Get(0).(func() (string, error)); ok {
+ return rf()
+ }
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ if rf, ok := ret.Get(1).(func() error); ok {
+ r1 = rf()
+ } else {
+ r1 = ret.Error(1)
+ }
+
+ return r0, r1
+}
+
+// URL provides a mock function with given fields:
+func (_m *Response) URL() string {
+ ret := _m.Called()
+
+ var r0 string
+ if rf, ok := ret.Get(0).(func() string); ok {
+ r0 = rf()
+ } else {
+ r0 = ret.Get(0).(string)
+ }
+
+ return r0
+}
+
+// NewResponse creates a new instance of Response. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// The first argument is typically a *testing.T value.
+func NewResponse(t interface {
+ mock.TestingT
+ Cleanup(func())
+}) *Response {
+ mock := &Response{}
+ mock.Mock.Test(t)
+
+ t.Cleanup(func() { mock.AssertExpectations(t) })
+
+ return mock
+}
diff --git a/pkg/awsconfig/awsconfig.go b/pkg/awsconfig/awsconfig.go
index 4d7317b19..b1bfef9dc 100644
--- a/pkg/awsconfig/awsconfig.go
+++ b/pkg/awsconfig/awsconfig.go
@@ -1,7 +1,6 @@
package awsconfig
import (
- "io/ioutil"
"os"
"path"
"path/filepath"
@@ -143,7 +142,7 @@ func (p *CredentialsProvider) ensureConfigExists() error {
logger.WithField("dir", dir).Debug("Dir created")
// create an base config file
- err = ioutil.WriteFile(filename, []byte("["+p.Profile+"]"), 0600)
+ err = os.WriteFile(filename, []byte("["+p.Profile+"]"), 0600)
if err != nil {
return err
}
diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go
index 79b5945b4..8051c8976 100644
--- a/pkg/cfg/cfg.go
+++ b/pkg/cfg/cfg.go
@@ -27,6 +27,9 @@ const (
// DefaultProfile this is the default profile name used to save the credentials in the aws cli
DefaultProfile = "saml"
+
+ // Environment Variable used to define the Keyring Backend for Linux based distro
+ KeyringBackEnvironmentVariableName = "SAML2AWS_KEYRING_BACKEND"
)
// IDPAccount saml IDP account
@@ -36,7 +39,10 @@ type IDPAccount struct {
URL string `ini:"url"`
Username string `ini:"username"`
Provider string `ini:"provider"`
+ BrowserType string `ini:"browser_type,omitempty"` // used by 'Browser' Provider
+ BrowserExecutablePath string `ini:"browser_executable_path,omitempty"` // used by 'Browser' Provider
MFA string `ini:"mfa"`
+ MFAIPAddress string `ini:"mfa_ip_address"` // used by OneLogin
SkipVerify bool `ini:"skip_verify"`
Timeout int `ini:"timeout"`
AmazonWebservicesURN string `ini:"aws_urn"`
@@ -52,8 +58,11 @@ type IDPAccount struct {
SAMLCache bool `ini:"saml_cache"`
SAMLCacheFile string `ini:"saml_cache_file"`
TargetURL string `ini:"target_url"`
- DisableRememberDevice bool `ini:"disable_remember_device"` // used by Okta
- DisableSessions bool `ini:"disable_sessions"` // used by Okta
+ DisableRememberDevice bool `ini:"disable_remember_device"` // used by Okta
+ DisableSessions bool `ini:"disable_sessions"` // used by Okta
+ DownloadBrowser bool `ini:"download_browser_driver"` // used by browser
+ BrowserDriverDir string `ini:"browser_driver_dir,omitempty"` // used by browser; hide from user if not set
+ Headless bool `ini:"headless"` // used by browser
Prompter string `ini:"prompter"`
}
diff --git a/pkg/cfg/cfg_test.go b/pkg/cfg/cfg_test.go
index b42802474..5a32a5c37 100644
--- a/pkg/cfg/cfg_test.go
+++ b/pkg/cfg/cfg_test.go
@@ -13,8 +13,28 @@ func TestNewConfigManagerNew(t *testing.T) {
cfgm, err := NewConfigManager("example/saml2aws.ini")
require.Nil(t, err)
+ require.NotNil(t, cfgm)
+}
+
+func TestIDPAccountString(t *testing.T) {
+ cfgm, err := NewConfigManager("example/saml2aws.ini")
+ require.Nil(t, err)
require.NotNil(t, cfgm)
+
+ idpAccount, err := cfgm.LoadIDPAccount("test123")
+ require.Nil(t, err)
+ s := idpAccount.String()
+ require.Contains(t, s, "urn:amazon:webservices\n")
+}
+
+func TestNewConfigManagerDefaultEmpty(t *testing.T) {
+ cfgm, err := NewConfigManager("")
+ require.Nil(t, err)
+ require.Contains(t, cfgm.configPath, ".saml2aws")
+ idpAccount, err := cfgm.LoadIDPAccount("foo")
+ require.Nil(t, err)
+ require.Equal(t, idpAccount.URL, "")
}
func TestNewConfigManagerLoad(t *testing.T) {
diff --git a/pkg/cookiejar/jar.go b/pkg/cookiejar/jar.go
index 62e5b26ec..2c3b569f8 100644
--- a/pkg/cookiejar/jar.go
+++ b/pkg/cookiejar/jar.go
@@ -18,9 +18,9 @@ import (
)
// PublicSuffixList provides the public suffix of a domain. For example:
-// - the public suffix of "example.com" is "com",
-// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
-// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
+// - the public suffix of "example.com" is "com",
+// - the public suffix of "foo1.foo2.foo3.co.uk" is "co.uk", and
+// - the public suffix of "bar.pvt.k12.ma.us" is "pvt.k12.ma.us".
//
// Implementations of PublicSuffixList must be safe for concurrent use by
// multiple goroutines.
diff --git a/pkg/cookiejar/jar_test.go b/pkg/cookiejar/jar_test.go
index fc1462d0d..5262d2154 100644
--- a/pkg/cookiejar/jar_test.go
+++ b/pkg/cookiejar/jar_test.go
@@ -20,8 +20,9 @@ var tNow = time.Date(2013, 1, 1, 12, 0, 0, 0, time.UTC)
// testPSL implements PublicSuffixList with just two rules: "co.uk"
// and the default rule "*".
// The implementation has two intentional bugs:
-// PublicSuffix("www.buggy.psl") == "xy"
-// PublicSuffix("www2.buggy.psl") == "com"
+//
+// PublicSuffix("www.buggy.psl") == "xy"
+// PublicSuffix("www2.buggy.psl") == "com"
type testPSL struct{}
func (testPSL) String() string {
@@ -358,13 +359,13 @@ func mustParseURL(s string) *url.URL {
}
// jarTest encapsulates the following actions on a jar:
-// 1. Perform SetCookies with fromURL and the cookies from setCookies.
-// (Done at time tNow + 0 ms.)
-// 2. Check that the entries in the jar matches content.
-// (Done at time tNow + 1001 ms.)
-// 3. For each query in tests: Check that Cookies with toURL yields the
-// cookies in want.
-// (Query n done at tNow + (n+2)*1001 ms.)
+// 1. Perform SetCookies with fromURL and the cookies from setCookies.
+// (Done at time tNow + 0 ms.)
+// 2. Check that the entries in the jar matches content.
+// (Done at time tNow + 1001 ms.)
+// 3. For each query in tests: Check that Cookies with toURL yields the
+// cookies in want.
+// (Query n done at tNow + (n+2)*1001 ms.)
type jarTest struct {
description string // The description of what this test is supposed to test
fromURL string // The full URL of the request from which Set-Cookie headers where received
diff --git a/pkg/creds/creds.go b/pkg/creds/creds.go
index e006216ba..5aa697103 100644
--- a/pkg/creds/creds.go
+++ b/pkg/creds/creds.go
@@ -4,6 +4,8 @@ package creds
type LoginDetails struct {
ClientID string // used by OneLogin
ClientSecret string // used by OneLogin
+ DownloadBrowser bool // used by Browser
+ MFAIPAddress string // used by OneLogin
Username string
Password string
MFAToken string
diff --git a/pkg/flags/flags.go b/pkg/flags/flags.go
index 7a3beb6fa..1848358c3 100644
--- a/pkg/flags/flags.go
+++ b/pkg/flags/flags.go
@@ -12,7 +12,10 @@ type CommonFlags struct {
ConfigFile string
IdpAccount string
IdpProvider string
+ BrowserType string
+ BrowserExecutablePath string
MFA string
+ MFAIPAddress string
MFAToken string
URL string
Username string
@@ -38,6 +41,7 @@ type CommonFlags struct {
// LoginExecFlags flags for the Login / Exec commands
type LoginExecFlags struct {
CommonFlags *CommonFlags
+ DownloadBrowser bool
Force bool
DuoMFAOption string
ExecProfile string
@@ -71,10 +75,22 @@ func ApplyFlagOverrides(commonFlags *CommonFlags, account *cfg.IDPAccount) {
account.Provider = commonFlags.IdpProvider
}
+ if commonFlags.BrowserType != "" {
+ account.BrowserType = commonFlags.BrowserType
+ }
+
+ if commonFlags.BrowserExecutablePath != "" {
+ account.BrowserExecutablePath = commonFlags.BrowserExecutablePath
+ }
+
if commonFlags.MFA != "" {
account.MFA = commonFlags.MFA
}
+ if commonFlags.MFAIPAddress != "" {
+ account.MFAIPAddress = commonFlags.MFAIPAddress
+ }
+
if commonFlags.AmazonWebservicesURN != "" {
account.AmazonWebservicesURN = commonFlags.AmazonWebservicesURN
}
diff --git a/pkg/page/form_test.go b/pkg/page/form_test.go
index 8b0656edc..00bfcdf3a 100644
--- a/pkg/page/form_test.go
+++ b/pkg/page/form_test.go
@@ -2,8 +2,8 @@ package page
import (
"bytes"
- "io/ioutil"
"net/url"
+ "os"
"testing"
"github.com/PuerkitoBio/goquery"
@@ -11,7 +11,7 @@ import (
)
func TestNewFormFromDocument(t *testing.T) {
- data, err := ioutil.ReadFile("example/multi-form.html")
+ data, err := os.ReadFile("example/multi-form.html")
require.Nil(t, err)
doc, err := goquery.NewDocumentFromReader(bytes.NewReader(data))
diff --git a/pkg/prompter/pinentry.go b/pkg/prompter/pinentry.go
index eea680ff3..86380fdd6 100644
--- a/pkg/prompter/pinentry.go
+++ b/pkg/prompter/pinentry.go
@@ -82,6 +82,11 @@ func (p *PinentryPrompter) Password(pr string) string {
return p.DefaultPrompter.Password(pr)
}
+// Display is runniner the default Cli Display
+func (p *PinentryPrompter) Display(pr string) {
+ p.DefaultPrompter.Display(pr)
+}
+
// Run wraps a pinentry run. It sends the query to pinentry via stdin and
// reads its stdout to determine the user PIN.
// Pinentry uses an Assuan protocol
diff --git a/pkg/prompter/pinentry_test.go b/pkg/prompter/pinentry_test.go
index 647af8ac0..7c4dfecec 100644
--- a/pkg/prompter/pinentry_test.go
+++ b/pkg/prompter/pinentry_test.go
@@ -36,6 +36,7 @@ type FakeDefaultPrompter struct {
CalledString bool
CalledStringRequired bool
CalledPassword bool
+ CalledDisplay bool
}
// all the functions to implement the Prompter interface
@@ -63,7 +64,9 @@ func (f *FakeDefaultPrompter) Password(p string) string {
f.CalledPassword = true
return ""
}
-
+func (f *FakeDefaultPrompter) Display(p string) {
+ f.CalledDisplay = true
+}
func TestValidateAndSetPrompterShouldFailWithWrongInput(t *testing.T) {
// backing up the current prompters for the other tests
@@ -125,6 +128,9 @@ func TestChecksPinentryPrompterDefault(t *testing.T) {
_ = p.Password("random")
assert.True(t, fakeDefaultPrompter.CalledPassword)
+
+ p.Display("random")
+ assert.True(t, fakeDefaultPrompter.CalledDisplay)
}
func TestChecksPinentryPrompterCallsPinentryForRequestSecurityCode(t *testing.T) {
diff --git a/pkg/prompter/prompter.go b/pkg/prompter/prompter.go
index 89bcb7791..2637c03b6 100644
--- a/pkg/prompter/prompter.go
+++ b/pkg/prompter/prompter.go
@@ -16,6 +16,7 @@ type Prompter interface {
StringRequired(string) string
String(string, string) string
Password(string) string
+ Display(string)
}
// SetPrompter configure an aternate prompter to the default one
@@ -79,3 +80,8 @@ func String(pr string, defaultValue string) string {
func Password(pr string) string {
return ActivePrompter.Password(pr)
}
+
+// Display prompt, no user input required
+func Display(pr string) {
+ ActivePrompter.Display(pr)
+}
diff --git a/pkg/prompter/survey.go b/pkg/prompter/survey.go
index 686a835fb..2ef5d223d 100644
--- a/pkg/prompter/survey.go
+++ b/pkg/prompter/survey.go
@@ -3,14 +3,29 @@ package prompter
import (
"errors"
"fmt"
+ "os"
survey "github.com/AlecAivazis/survey/v2"
+ survey_terminal "github.com/AlecAivazis/survey/v2/terminal"
)
+// outputWriter is where for all prompts will be printed. Defaults to os.Stder.
+var outputWriter survey_terminal.FileWriter = os.Stderr
+
// CliPrompter used to prompt for cli input
type CliPrompter struct {
}
+// SetOutputWriter sets the output writer to use for all survey operations
+func SetOutputWriter(writer survey_terminal.FileWriter) {
+ outputWriter = writer
+}
+
+// stdioOption returns the IO option to use for survey functions
+func stdioOption() survey.AskOpt {
+ return survey.WithStdio(os.Stdin, outputWriter, os.Stderr)
+}
+
// NewCli builds a new cli prompter
func NewCli() *CliPrompter {
return &CliPrompter{}
@@ -22,7 +37,7 @@ func (cli *CliPrompter) RequestSecurityCode(pattern string) string {
prompt := &survey.Input{
Message: fmt.Sprintf("Security Token [%s]", pattern),
}
- _ = survey.AskOne(prompt, &token, survey.WithValidator(survey.Required))
+ _ = survey.AskOne(prompt, &token, survey.WithValidator(survey.Required), stdioOption())
return token
}
@@ -34,7 +49,7 @@ func (cli *CliPrompter) ChooseWithDefault(pr string, defaultValue string, option
Options: options,
Default: defaultValue,
}
- _ = survey.AskOne(prompt, &selected, survey.WithValidator(survey.Required))
+ _ = survey.AskOne(prompt, &selected, survey.WithValidator(survey.Required), stdioOption())
// return the selected element index
for i, option := range options {
@@ -52,7 +67,7 @@ func (cli *CliPrompter) Choose(pr string, options []string) int {
Message: pr,
Options: options,
}
- _ = survey.AskOne(prompt, &selected, survey.WithValidator(survey.Required))
+ _ = survey.AskOne(prompt, &selected, survey.WithValidator(survey.Required), stdioOption())
// return the selected element index
for i, option := range options {
@@ -63,14 +78,14 @@ func (cli *CliPrompter) Choose(pr string, options []string) int {
return 0
}
-// StringRequired prompt for string which is required
+// String prompt for string with a default
func (cli *CliPrompter) String(pr string, defaultValue string) string {
val := ""
prompt := &survey.Input{
Message: pr,
Default: defaultValue,
}
- _ = survey.AskOne(prompt, &val)
+ _ = survey.AskOne(prompt, &val, stdioOption())
return val
}
@@ -80,7 +95,7 @@ func (cli *CliPrompter) StringRequired(pr string) string {
prompt := &survey.Input{
Message: pr,
}
- _ = survey.AskOne(prompt, &val, survey.WithValidator(survey.Required))
+ _ = survey.AskOne(prompt, &val, survey.WithValidator(survey.Required), stdioOption())
return val
}
@@ -90,6 +105,10 @@ func (cli *CliPrompter) Password(pr string) string {
prompt := &survey.Password{
Message: pr,
}
- _ = survey.AskOne(prompt, &val)
+ _ = survey.AskOne(prompt, &val, stdioOption())
return val
}
+
+func (cli *CliPrompter) Display(pr string) {
+ _, _ = os.Stderr.WriteString(pr + "\n")
+}
diff --git a/pkg/provider/aad/aad.go b/pkg/provider/aad/aad.go
index 0c2607097..c154f855f 100644
--- a/pkg/provider/aad/aad.go
+++ b/pkg/provider/aad/aad.go
@@ -6,8 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
- "io/ioutil"
- "log"
"net/http"
"net/url"
"strings"
@@ -15,13 +13,16 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+
"github.com/versent/saml2aws/v2/pkg/cfg"
"github.com/versent/saml2aws/v2/pkg/creds"
"github.com/versent/saml2aws/v2/pkg/prompter"
"github.com/versent/saml2aws/v2/pkg/provider"
- "golang.org/x/net/html"
)
+var logger = logrus.WithField("provider", "AzureAD")
+
// Client wrapper around AzureAD enabling authentication and retrieval of assertions
type Client struct {
provider.ValidateBase
@@ -30,440 +31,76 @@ type Client struct {
idpAccount *cfg.IDPAccount
}
-// Autogenerate startSAML Response struct
-// some case, some fields is not exists
-type startSAMLResponse struct {
- FShowPersistentCookiesWarning bool `json:"fShowPersistentCookiesWarning"`
- URLMsaLogout string `json:"urlMsaLogout"`
- ShowCantAccessAccountLink bool `json:"showCantAccessAccountLink"`
- URLGitHubFed string `json:"urlGitHubFed"`
- FShowSignInWithGitHubOnlyOnCredPicker bool `json:"fShowSignInWithGitHubOnlyOnCredPicker"`
- FEnableShowResendCode bool `json:"fEnableShowResendCode"`
- IShowResendCodeDelay int `json:"iShowResendCodeDelay"`
- SSMSCtryPhoneData string `json:"sSMSCtryPhoneData"`
- FUseInlinePhoneNumber bool `json:"fUseInlinePhoneNumber"`
- URLSessionState string `json:"urlSessionState"`
- URLResetPassword string `json:"urlResetPassword"`
- URLMsaResetPassword string `json:"urlMsaResetPassword"`
- URLLogin string `json:"urlLogin"`
- URLSignUp string `json:"urlSignUp"`
- URLGetCredentialType string `json:"urlGetCredentialType"`
- URLGetOneTimeCode string `json:"urlGetOneTimeCode"`
- URLLogout string `json:"urlLogout"`
- URLForget string `json:"urlForget"`
- URLDisambigRename string `json:"urlDisambigRename"`
- URLGoToAADError string `json:"urlGoToAADError"`
- URLDssoStatus string `json:"urlDssoStatus"`
- URLFidoHelp string `json:"urlFidoHelp"`
- URLFidoLogin string `json:"urlFidoLogin"`
- URLPostAad string `json:"urlPostAad"`
- URLPostMsa string `json:"urlPostMsa"`
- URLPIAEndAuth string `json:"urlPIAEndAuth"`
- FCBShowSignUp bool `json:"fCBShowSignUp"`
- FKMSIEnabled bool `json:"fKMSIEnabled"`
- ILoginMode int `json:"iLoginMode"`
- FAllowPhoneSignIn bool `json:"fAllowPhoneSignIn"`
- FAllowPhoneInput bool `json:"fAllowPhoneInput"`
- FAllowSkypeNameLogin bool `json:"fAllowSkypeNameLogin"`
- IMaxPollErrors int `json:"iMaxPollErrors"`
- IPollingTimeout int `json:"iPollingTimeout"`
- SrsSuccess bool `json:"srsSuccess"`
- FShowSwitchUser bool `json:"fShowSwitchUser"`
- ArrValErrs []string `json:"arrValErrs"`
- SErrorCode string `json:"sErrorCode"`
- SErrTxt string `json:"sErrTxt"`
- SResetPasswordPrefillParam string `json:"sResetPasswordPrefillParam"`
- OnPremPasswordValidationConfig struct {
- IsUserRealmPrecheckEnabled bool `json:"isUserRealmPrecheckEnabled"`
- } `json:"onPremPasswordValidationConfig"`
- FSwitchDisambig bool `json:"fSwitchDisambig"`
- OCancelPostParams struct {
- Error string `json:"error"`
- ErrorSubcode string `json:"error_subcode"`
- State string `json:"state"`
- } `json:"oCancelPostParams"`
- IAllowedIdentities int `json:"iAllowedIdentities"`
- IRemoteNgcPollingType int `json:"iRemoteNgcPollingType"`
- IsGlobalTenant bool `json:"isGlobalTenant"`
- FIsFidoSupported bool `json:"fIsFidoSupported"`
- FUseNewNoPasswordTypes bool `json:"fUseNewNoPasswordTypes"`
- IMaxStackForKnockoutAsyncComponents int `json:"iMaxStackForKnockoutAsyncComponents"`
- StrCopyrightTxt string `json:"strCopyrightTxt"`
- FShowButtons bool `json:"fShowButtons"`
- URLCdn string `json:"urlCdn"`
- URLFooterTOU string `json:"urlFooterTOU"`
- URLFooterPrivacy string `json:"urlFooterPrivacy"`
- URLPost string `json:"urlPost"`
- URLRefresh string `json:"urlRefresh"`
- URLCancel string `json:"urlCancel"`
- IPawnIcon int `json:"iPawnIcon"`
- IPollingInterval int `json:"iPollingInterval"`
- SPOSTUsername string `json:"sPOST_Username"`
- SFT string `json:"sFT"`
- SFTName string `json:"sFTName"`
- SSessionIdentifierName string `json:"sSessionIdentifierName"`
- SCtx string `json:"sCtx"`
- IProductIcon int `json:"iProductIcon"`
- URLReportPageLoad string `json:"urlReportPageLoad"`
- StaticTenantBranding interface{} `json:"staticTenantBranding"`
- OAppCobranding struct {
- } `json:"oAppCobranding"`
- IBackgroundImage int `json:"iBackgroundImage"`
- ArrSessions []interface{} `json:"arrSessions"`
- FUseConstantPolling bool `json:"fUseConstantPolling"`
- FUseFlowTokenAsCanary bool `json:"fUseFlowTokenAsCanary"`
- FApplicationInsightsEnabled bool `json:"fApplicationInsightsEnabled"`
- IApplicationInsightsEnabledPercentage int `json:"iApplicationInsightsEnabledPercentage"`
- URLSetDebugMode string `json:"urlSetDebugMode"`
- FEnableCSSAnimation bool `json:"fEnableCssAnimation"`
- FAllowGrayOutLightBox bool `json:"fAllowGrayOutLightBox"`
- FIsRemoteNGCSupported bool `json:"fIsRemoteNGCSupported"`
- Scid int `json:"scid"`
- Hpgact int `json:"hpgact"`
- Hpgid int `json:"hpgid"`
- Pgid string `json:"pgid"`
- APICanary string `json:"apiCanary"`
- Canary string `json:"canary"`
- CorrelationID string `json:"correlationId"`
- SessionID string `json:"sessionId"`
- Locale struct {
- Mkt string `json:"mkt"`
- Lcid int `json:"lcid"`
- } `json:"locale"`
- SlMaxRetry int `json:"slMaxRetry"`
- SlReportFailure bool `json:"slReportFailure"`
- Strings struct {
- Desktopsso struct {
- Authenticatingmessage string `json:"authenticatingmessage"`
- } `json:"desktopsso"`
- } `json:"strings"`
- Enums struct {
- ClientMetricsModes struct {
- None int `json:"None"`
- SubmitOnPost int `json:"SubmitOnPost"`
- SubmitOnRedirect int `json:"SubmitOnRedirect"`
- InstrumentPlt int `json:"InstrumentPlt"`
- } `json:"ClientMetricsModes"`
- } `json:"enums"`
- Urls struct {
- Instr struct {
- Pageload string `json:"pageload"`
- Dssostatus string `json:"dssostatus"`
- } `json:"instr"`
- } `json:"urls"`
- Browser struct {
- Ltr int `json:"ltr"`
- Other int `json:"_Other"`
- Full int `json:"Full"`
- REOther int `json:"RE_Other"`
- B struct {
- Name string `json:"name"`
- Major int `json:"major"`
- Minor int `json:"minor"`
- } `json:"b"`
- Os struct {
- Name string `json:"name"`
- Version string `json:"version"`
- } `json:"os"`
- V int `json:"V"`
- } `json:"browser"`
- Watson struct {
- URL string `json:"url"`
- Bundle string `json:"bundle"`
- Sbundle string `json:"sbundle"`
- Fbundle string `json:"fbundle"`
- ResetErrorPeriod int `json:"resetErrorPeriod"`
- MaxCorsErrors int `json:"maxCorsErrors"`
- MaxInjectErrors int `json:"maxInjectErrors"`
- MaxErrors int `json:"maxErrors"`
- MaxTotalErrors int `json:"maxTotalErrors"`
- ExpSrcs []string `json:"expSrcs"`
- EnvErrorRedirect bool `json:"envErrorRedirect"`
- EnvErrorURL string `json:"envErrorUrl"`
- } `json:"watson"`
- Loader struct {
- CdnRoots []string `json:"cdnRoots"`
- } `json:"loader"`
- ServerDetails struct {
- Slc string `json:"slc"`
- Dc string `json:"dc"`
- Ri string `json:"ri"`
- Ver struct {
- V []int `json:"v"`
- } `json:"ver"`
- Rt string `json:"rt"`
- Et int `json:"et"`
- } `json:"serverDetails"`
- Country string `json:"country"`
- FBreakBrandingSigninString bool `json:"fBreakBrandingSigninString"`
- Bsso struct {
- Type string `json:"type"`
- Reason string `json:"reason"`
- } `json:"bsso"`
- URLNoCookies string `json:"urlNoCookies"`
- FTrimChromeBssoURL bool `json:"fTrimChromeBssoUrl"`
+// Autogenrated Converged Response struct
+// for some cases, some fields may not exist
+type ConvergedResponse struct {
+ URLGetCredentialType string `json:"urlGetCredentialType"`
+ ArrUserProofs []userProof `json:"arrUserProofs"`
+ URLSkipMfaRegistration string `json:"urlSkipMfaRegistration"`
+ OPerAuthPollingInterval map[string]float64 `json:"oPerAuthPollingInterval"`
+ URLBeginAuth string `json:"urlBeginAuth"`
+ URLEndAuth string `json:"urlEndAuth"`
+ URLPost string `json:"urlPost"`
+ SErrorCode string `json:"sErrorCode"`
+ SErrTxt string `json:"sErrTxt"`
+ SPOSTUsername string `json:"sPOST_Username"`
+ SFT string `json:"sFT"`
+ SFTName string `json:"sFTName"`
+ SCtx string `json:"sCtx"`
+ Hpgact int `json:"hpgact"`
+ Hpgid int `json:"hpgid"`
+ Pgid string `json:"pgid"`
+ APICanary string `json:"apiCanary"`
+ Canary string `json:"canary"`
+ CorrelationID string `json:"correlationId"`
+ SessionID string `json:"sessionId"`
}
-// Autogenerate password login response
-// some case, some fields is not exists
-type passwordLoginResponse struct {
- ArrUserProofs []userProof `json:"arrUserProofs"`
- FHideIHaveCodeLink bool `json:"fHideIHaveCodeLink"`
- OPerAuthPollingInterval map[string]float64 `json:"oPerAuthPollingInterval"`
- FProofIndexedByType bool `json:"fProofIndexedByType"`
- URLBeginAuth string `json:"urlBeginAuth"`
- URLEndAuth string `json:"urlEndAuth"`
- ISAMode int `json:"iSAMode"`
- ITrustedDeviceCheckboxConfig int `json:"iTrustedDeviceCheckboxConfig"`
- IMaxPollAttempts int `json:"iMaxPollAttempts"`
- IPollingTimeout int `json:"iPollingTimeout"`
- IPollingBackoffInterval float64 `json:"iPollingBackoffInterval"`
- IRememberMfaDuration float64 `json:"iRememberMfaDuration"`
- STrustedDeviceCheckboxName string `json:"sTrustedDeviceCheckboxName"`
- SAuthMethodInputFieldName string `json:"sAuthMethodInputFieldName"`
- ISAOtcLength int `json:"iSAOtcLength"`
- ITotpOtcLength int `json:"iTotpOtcLength"`
- URLMoreInfo string `json:"urlMoreInfo"`
- FShowViewDetailsLink bool `json:"fShowViewDetailsLink"`
- FAlwaysUpdateFTInSasEnd bool `json:"fAlwaysUpdateFTInSasEnd"`
- IMaxStackForKnockoutAsyncComponents int `json:"iMaxStackForKnockoutAsyncComponents"`
- StrCopyrightTxt string `json:"strCopyrightTxt"`
- FShowButtons bool `json:"fShowButtons"`
- URLCdn string `json:"urlCdn"`
- URLFooterTOU string `json:"urlFooterTOU"`
- URLFooterPrivacy string `json:"urlFooterPrivacy"`
- URLPost string `json:"urlPost"`
- URLCancel string `json:"urlCancel"`
- IPawnIcon int `json:"iPawnIcon"`
- IPollingInterval int `json:"iPollingInterval"`
- SPOSTUsername string `json:"sPOST_Username"`
- SFT string `json:"sFT"`
- SFTName string `json:"sFTName"`
- SCtx string `json:"sCtx"`
- DynamicTenantBranding []struct {
- Locale int `json:"Locale"`
- Illustration string `json:"Illustration"`
- UserIDLabel string `json:"UserIdLabel"`
- KeepMeSignedInDisabled bool `json:"KeepMeSignedInDisabled"`
- UseTransparentLightBox bool `json:"UseTransparentLightBox"`
- } `json:"dynamicTenantBranding"`
- OAppCobranding struct {
- } `json:"oAppCobranding"`
- IBackgroundImage int `json:"iBackgroundImage"`
- FUseConstantPolling bool `json:"fUseConstantPolling"`
- FUseFlowTokenAsCanary bool `json:"fUseFlowTokenAsCanary"`
- FApplicationInsightsEnabled bool `json:"fApplicationInsightsEnabled"`
- IApplicationInsightsEnabledPercentage int `json:"iApplicationInsightsEnabledPercentage"`
- URLSetDebugMode string `json:"urlSetDebugMode"`
- FEnableCSSAnimation bool `json:"fEnableCssAnimation"`
- FAllowGrayOutLightBox bool `json:"fAllowGrayOutLightBox"`
- FIsRemoteNGCSupported bool `json:"fIsRemoteNGCSupported"`
- Scid int `json:"scid"`
- Hpgact int `json:"hpgact"`
- Hpgid int `json:"hpgid"`
- Pgid string `json:"pgid"`
- APICanary string `json:"apiCanary"`
- Canary string `json:"canary"`
- CorrelationID string `json:"correlationId"`
- SessionID string `json:"sessionId"`
- Locale struct {
- Mkt string `json:"mkt"`
- Lcid int `json:"lcid"`
- } `json:"locale"`
- SlMaxRetry int `json:"slMaxRetry"`
- SlReportFailure bool `json:"slReportFailure"`
- Strings struct {
- Desktopsso struct {
- Authenticatingmessage string `json:"authenticatingmessage"`
- } `json:"desktopsso"`
- } `json:"strings"`
- Enums struct {
- ClientMetricsModes struct {
- None int `json:"None"`
- SubmitOnPost int `json:"SubmitOnPost"`
- SubmitOnRedirect int `json:"SubmitOnRedirect"`
- InstrumentPlt int `json:"InstrumentPlt"`
- } `json:"ClientMetricsModes"`
- } `json:"enums"`
- Urls struct {
- Instr struct {
- Pageload string `json:"pageload"`
- Dssostatus string `json:"dssostatus"`
- } `json:"instr"`
- } `json:"urls"`
- Browser struct {
- Ltr int `json:"ltr"`
- Other int `json:"_Other"`
- Full int `json:"Full"`
- REOther int `json:"RE_Other"`
- B struct {
- Name string `json:"name"`
- Major int `json:"major"`
- Minor int `json:"minor"`
- } `json:"b"`
- Os struct {
- Name string `json:"name"`
- Version string `json:"version"`
- } `json:"os"`
- V int `json:"V"`
- } `json:"browser"`
- Watson struct {
- URL string `json:"url"`
- Bundle string `json:"bundle"`
- Sbundle string `json:"sbundle"`
- Fbundle string `json:"fbundle"`
- ResetErrorPeriod int `json:"resetErrorPeriod"`
- MaxCorsErrors int `json:"maxCorsErrors"`
- MaxInjectErrors int `json:"maxInjectErrors"`
- MaxErrors int `json:"maxErrors"`
- MaxTotalErrors int `json:"maxTotalErrors"`
- ExpSrcs []string `json:"expSrcs"`
- EnvErrorRedirect bool `json:"envErrorRedirect"`
- EnvErrorURL string `json:"envErrorUrl"`
- } `json:"watson"`
- Loader struct {
- CdnRoots []string `json:"cdnRoots"`
- } `json:"loader"`
- ServerDetails struct {
- Slc string `json:"slc"`
- Dc string `json:"dc"`
- Ri string `json:"ri"`
- Ver struct {
- V []int `json:"v"`
- } `json:"ver"`
- Rt string `json:"rt"`
- Et int `json:"et"`
- } `json:"serverDetails"`
- Country string `json:"country"`
- FBreakBrandingSigninString bool `json:"fBreakBrandingSigninString"`
- URLNoCookies string `json:"urlNoCookies"`
- FTrimChromeBssoURL bool `json:"fTrimChromeBssoUrl"`
+// Autogenerated GetCredentialType Request struct
+// for some cases, some fields may not exist
+type GetCredentialTypeRequest struct {
+ Username string `json:"username"`
+ IsOtherIdpSupported bool `json:"isOtherIdpSupported"`
+ CheckPhones bool `json:"checkPhones"`
+ IsRemoteNGCSupported bool `json:"isRemoteNGCSupported"`
+ IsCookieBannerShown bool `json:"isCookieBannerShown"`
+ IsFidoSupported bool `json:"isFidoSupported"`
+ OriginalRequest string `json:"originalRequest"`
+ Country string `json:"country"`
+ Forceotclogin bool `json:"forceotclogin"`
+ IsExternalFederationDisallowed bool `json:"isExternalFederationDisallowed"`
+ IsRemoteConnectSupported bool `json:"isRemoteConnectSupported"`
+ FederationFlags int `json:"federationFlags"`
+ IsSignup bool `json:"isSignup"`
+ FlowToken string `json:"flowToken"`
+ IsAccessPassSupported bool `json:"isAccessPassSupported"`
}
-// Autogenerated skip mfa login response
-type SkipMfaResponse struct {
- URLPostRedirect string `json:"urlPostRedirect"`
- URLSkipMfaRegistration string `json:"urlSkipMfaRegistration"`
- URLMoreInfo string `json:"urlMoreInfo"`
- SProofUpToken string `json:"sProofUpToken"`
- SProofUpTokenName string `json:"sProofUpTokenName"`
- SProofUpAuthState string `json:"sProofUpAuthState"`
- SCanaryToken string `json:"sCanaryToken"`
- IRemainingDaysToSkipMfaRegistration int `json:"iRemainingDaysToSkipMfaRegistration"`
- IMaxStackForKnockoutAsyncComponents int `json:"iMaxStackForKnockoutAsyncComponents"`
- StrCopyrightTxt string `json:"strCopyrightTxt"`
- FShowButtons bool `json:"fShowButtons"`
- URLCdn string `json:"urlCdn"`
- URLFooterTOU string `json:"urlFooterTOU"`
- URLFooterPrivacy string `json:"urlFooterPrivacy"`
- URLPost string `json:"urlPost"`
- URLCancel string `json:"urlCancel"`
- IPawnIcon int `json:"iPawnIcon"`
- SPOSTUsername string `json:"sPOST_Username"`
- SFT string `json:"sFT"`
- SFTName string `json:"sFTName"`
- SCanaryTokenName string `json:"sCanaryTokenName"`
- DynamicTenantBranding []struct {
- Locale int `json:"Locale"`
- Illustration string `json:"Illustration"`
- UserIDLabel string `json:"UserIdLabel"`
- KeepMeSignedInDisabled bool `json:"KeepMeSignedInDisabled"`
- UseTransparentLightBox bool `json:"UseTransparentLightBox"`
- } `json:"dynamicTenantBranding"`
- OAppCobranding struct {
- } `json:"oAppCobranding"`
- IBackgroundImage int `json:"iBackgroundImage"`
- FUseConstantPolling bool `json:"fUseConstantPolling"`
- FUseFlowTokenAsCanary bool `json:"fUseFlowTokenAsCanary"`
- FApplicationInsightsEnabled bool `json:"fApplicationInsightsEnabled"`
- IApplicationInsightsEnabledPercentage int `json:"iApplicationInsightsEnabledPercentage"`
- URLSetDebugMode string `json:"urlSetDebugMode"`
- FEnableCSSAnimation bool `json:"fEnableCssAnimation"`
- FAllowGrayOutLightBox bool `json:"fAllowGrayOutLightBox"`
- FIsRemoteNGCSupported bool `json:"fIsRemoteNGCSupported"`
- Scid int `json:"scid"`
- Hpgact int `json:"hpgact"`
- Hpgid int `json:"hpgid"`
- Pgid string `json:"pgid"`
- APICanary string `json:"apiCanary"`
- Canary string `json:"canary"`
- CorrelationID string `json:"correlationId"`
- SessionID string `json:"sessionId"`
- Locale struct {
- Mkt string `json:"mkt"`
- Lcid int `json:"lcid"`
- } `json:"locale"`
- SlMaxRetry int `json:"slMaxRetry"`
- SlReportFailure bool `json:"slReportFailure"`
- Strings struct {
- Desktopsso struct {
- Authenticatingmessage string `json:"authenticatingmessage"`
- } `json:"desktopsso"`
- } `json:"strings"`
- Enums struct {
- ClientMetricsModes struct {
- None int `json:"None"`
- SubmitOnPost int `json:"SubmitOnPost"`
- SubmitOnRedirect int `json:"SubmitOnRedirect"`
- InstrumentPlt int `json:"InstrumentPlt"`
- } `json:"ClientMetricsModes"`
- } `json:"enums"`
- Urls struct {
- Instr struct {
- Pageload string `json:"pageload"`
- Dssostatus string `json:"dssostatus"`
- } `json:"instr"`
- } `json:"urls"`
- Browser struct {
- Ltr int `json:"ltr"`
- Other int `json:"_Other"`
- Full int `json:"Full"`
- REOther int `json:"RE_Other"`
- B struct {
- Name string `json:"name"`
- Major int `json:"major"`
- Minor int `json:"minor"`
- } `json:"b"`
- Os struct {
- Name string `json:"name"`
- Version string `json:"version"`
- } `json:"os"`
- V int `json:"V"`
- } `json:"browser"`
- Watson struct {
- URL string `json:"url"`
- Bundle string `json:"bundle"`
- Sbundle string `json:"sbundle"`
- Fbundle string `json:"fbundle"`
- ResetErrorPeriod int `json:"resetErrorPeriod"`
- MaxCorsErrors int `json:"maxCorsErrors"`
- MaxInjectErrors int `json:"maxInjectErrors"`
- MaxErrors int `json:"maxErrors"`
- MaxTotalErrors int `json:"maxTotalErrors"`
- ExpSrcs []string `json:"expSrcs"`
- EnvErrorRedirect bool `json:"envErrorRedirect"`
- EnvErrorURL string `json:"envErrorUrl"`
- } `json:"watson"`
- Loader struct {
- CdnRoots []string `json:"cdnRoots"`
- } `json:"loader"`
- ServerDetails struct {
- Slc string `json:"slc"`
- Dc string `json:"dc"`
- Ri string `json:"ri"`
- Ver struct {
- V []int `json:"v"`
- } `json:"ver"`
- Rt string `json:"rt"`
- Et int `json:"et"`
- } `json:"serverDetails"`
- Country string `json:"country"`
- FBreakBrandingSigninString bool `json:"fBreakBrandingSigninString"`
- URLNoCookies string `json:"urlNoCookies"`
- FTrimChromeBssoURL bool `json:"fTrimChromeBssoUrl"`
+// Autogenerated GetCredentialType Response struct
+// for some cases, some fields may not exist
+type GetCredentialTypeResponse struct {
+ Username string `json:"Username"`
+ Display string `json:"Display"`
+ IfExistsResult int `json:"IfExistsResult"`
+ IsUnmanaged bool `json:"IsUnmanaged"`
+ ThrottleStatus int `json:"ThrottleStatus"`
+ Credentials struct {
+ PrefCredential int `json:"PrefCredential"`
+ HasPassword bool `json:"HasPassword"`
+ RemoteNgcParams interface{} `json:"RemoteNgcParams"`
+ FidoParams interface{} `json:"FidoParams"`
+ SasParams interface{} `json:"SasParams"`
+ CertAuthParams interface{} `json:"CertAuthParams"`
+ GoogleParams interface{} `json:"GoogleParams"`
+ FacebookParams interface{} `json:"FacebookParams"`
+ FederationRedirectURL string `json:"FederationRedirectUrl"`
+ } `json:"Credentials"`
+ FlowToken string `json:"FlowToken"`
+ IsSignupDisallowed bool `json:"IsSignupDisallowed"`
+ APICanary string `json:"apiCanary"`
}
-// mfa request
+// MFA Request struct
type mfaRequest struct {
AuthMethodID string `json:"AuthMethodId"`
Method string `json:"Method"`
@@ -473,7 +110,7 @@ type mfaRequest struct {
AdditionalAuthData string `json:"AdditionalAuthData,omitempty"`
}
-// mfa response
+// MFA Response struct
type mfaResponse struct {
Success bool `json:"Success"`
ResultValue string `json:"ResultValue"`
@@ -486,122 +123,7 @@ type mfaResponse struct {
SessionID string `json:"SessionId"`
CorrelationID string `json:"CorrelationId"`
Timestamp time.Time `json:"Timestamp"`
-}
-
-// Autogenerate ProcessAuth response
-// some case, some fields is not exists
-type processAuthResponse struct {
- IMaxStackForKnockoutAsyncComponents int `json:"iMaxStackForKnockoutAsyncComponents"`
- StrCopyrightTxt string `json:"strCopyrightTxt"`
- FShowButtons bool `json:"fShowButtons"`
- URLCdn string `json:"urlCdn"`
- URLFooterTOU string `json:"urlFooterTOU"`
- URLFooterPrivacy string `json:"urlFooterPrivacy"`
- URLPost string `json:"urlPost"`
- IPawnIcon int `json:"iPawnIcon"`
- SPOSTUsername string `json:"sPOST_Username"`
- SFT string `json:"sFT"`
- SFTName string `json:"sFTName"`
- SCtx string `json:"sCtx"`
- SCanaryTokenName string `json:"sCanaryTokenName"`
- DynamicTenantBranding []struct {
- Locale int `json:"Locale"`
- Illustration string `json:"Illustration"`
- UserIDLabel string `json:"UserIdLabel"`
- KeepMeSignedInDisabled bool `json:"KeepMeSignedInDisabled"`
- UseTransparentLightBox bool `json:"UseTransparentLightBox"`
- } `json:"dynamicTenantBranding"`
- OAppCobranding struct {
- } `json:"oAppCobranding"`
- IBackgroundImage int `json:"iBackgroundImage"`
- FUseConstantPolling bool `json:"fUseConstantPolling"`
- FUseFlowTokenAsCanary bool `json:"fUseFlowTokenAsCanary"`
- FApplicationInsightsEnabled bool `json:"fApplicationInsightsEnabled"`
- IApplicationInsightsEnabledPercentage int `json:"iApplicationInsightsEnabledPercentage"`
- URLSetDebugMode string `json:"urlSetDebugMode"`
- FEnableCSSAnimation bool `json:"fEnableCssAnimation"`
- FAllowGrayOutLightBox bool `json:"fAllowGrayOutLightBox"`
- FIsRemoteNGCSupported bool `json:"fIsRemoteNGCSupported"`
- Scid int `json:"scid"`
- Hpgact int `json:"hpgact"`
- Hpgid int `json:"hpgid"`
- Pgid string `json:"pgid"`
- APICanary string `json:"apiCanary"`
- Canary string `json:"canary"`
- CorrelationID string `json:"correlationId"`
- SessionID string `json:"sessionId"`
- Locale struct {
- Mkt string `json:"mkt"`
- Lcid int `json:"lcid"`
- } `json:"locale"`
- SlMaxRetry int `json:"slMaxRetry"`
- SlReportFailure bool `json:"slReportFailure"`
- Strings struct {
- Desktopsso struct {
- Authenticatingmessage string `json:"authenticatingmessage"`
- } `json:"desktopsso"`
- } `json:"strings"`
- Enums struct {
- ClientMetricsModes struct {
- None int `json:"None"`
- SubmitOnPost int `json:"SubmitOnPost"`
- SubmitOnRedirect int `json:"SubmitOnRedirect"`
- InstrumentPlt int `json:"InstrumentPlt"`
- } `json:"ClientMetricsModes"`
- } `json:"enums"`
- Urls struct {
- Instr struct {
- Pageload string `json:"pageload"`
- Dssostatus string `json:"dssostatus"`
- } `json:"instr"`
- } `json:"urls"`
- Browser struct {
- Ltr int `json:"ltr"`
- Other int `json:"_Other"`
- Full int `json:"Full"`
- REOther int `json:"RE_Other"`
- B struct {
- Name string `json:"name"`
- Major int `json:"major"`
- Minor int `json:"minor"`
- } `json:"b"`
- Os struct {
- Name string `json:"name"`
- Version string `json:"version"`
- } `json:"os"`
- V int `json:"V"`
- } `json:"browser"`
- Watson struct {
- URL string `json:"url"`
- Bundle string `json:"bundle"`
- Sbundle string `json:"sbundle"`
- Fbundle string `json:"fbundle"`
- ResetErrorPeriod int `json:"resetErrorPeriod"`
- MaxCorsErrors int `json:"maxCorsErrors"`
- MaxInjectErrors int `json:"maxInjectErrors"`
- MaxErrors int `json:"maxErrors"`
- MaxTotalErrors int `json:"maxTotalErrors"`
- ExpSrcs []string `json:"expSrcs"`
- EnvErrorRedirect bool `json:"envErrorRedirect"`
- EnvErrorURL string `json:"envErrorUrl"`
- } `json:"watson"`
- Loader struct {
- CdnRoots []string `json:"cdnRoots"`
- } `json:"loader"`
- ServerDetails struct {
- Slc string `json:"slc"`
- Dc string `json:"dc"`
- Ri string `json:"ri"`
- Ver struct {
- V []int `json:"v"`
- } `json:"ver"`
- Rt string `json:"rt"`
- Et int `json:"et"`
- } `json:"serverDetails"`
- Country string `json:"country"`
- FBreakBrandingSigninString bool `json:"fBreakBrandingSigninString"`
- URLNoCookies string `json:"urlNoCookies"`
- FTrimChromeBssoURL bool `json:"fTrimChromeBssoUrl"`
+ Entropy int `json:"Entropy"`
}
// A given method for a user to prove their indentity
@@ -633,337 +155,299 @@ func New(idpAccount *cfg.IDPAccount) (*Client, error) {
// Authenticate to AzureAD and return the data from the body of the SAML assertion.
func (ac *Client) Authenticate(loginDetails *creds.LoginDetails) (string, error) {
-
var samlAssertion string
var res *http.Response
+ var err error
+ var resBody []byte
+ var resBodyStr string
+ var convergedResponse *ConvergedResponse
// idpAccount.URL = https://account.activedirectory.windowsazure.com
// startSAML
startURL := fmt.Sprintf("%s/applications/redirecttofederatedapplication.aspx?Operation=LinkedSignIn&applicationId=%s", ac.idpAccount.URL, ac.idpAccount.AppID)
- res, err := ac.client.Get(startURL)
+ res, err = ac.client.Get(startURL)
if err != nil {
- return samlAssertion, errors.Wrap(err, "error retrieving form")
+ return samlAssertion, errors.Wrap(err, "error retrieving entry URL")
+ }
+
+AuthProcessor:
+ for {
+ resBody, _ = io.ReadAll(res.Body)
+ resBodyStr = string(resBody)
+ // reset res.Body so it can be read again later if required
+ res.Body = io.NopCloser(bytes.NewBuffer(resBody))
+
+ switch {
+ case strings.Contains(resBodyStr, "ConvergedSignIn"):
+ logger.Debug("processing ConvergedSignIn")
+ res, err = ac.processConvergedSignIn(res, resBodyStr, loginDetails)
+ case strings.Contains(resBodyStr, "ConvergedProofUpRedirect"):
+ logger.Debug("processing ConvergedProofUpRedirect")
+ res, err = ac.processConvergedProofUpRedirect(res, resBodyStr)
+ case strings.Contains(resBodyStr, "KmsiInterrupt"):
+ logger.Debug("processing KmsiInterrupt")
+ res, err = ac.processKmsiInterrupt(res, resBodyStr)
+ case strings.Contains(resBodyStr, "ConvergedTFA"):
+ logger.Debug("processing ConvergedTFA")
+ res, err = ac.processConvergedTFA(res, resBodyStr)
+ case strings.Contains(resBodyStr, "SAMLRequest"):
+ logger.Debug("processing SAMLRequest")
+ res, err = ac.processSAMLRequest(res, resBodyStr)
+ case ac.isHiddenForm(resBodyStr):
+ if samlAssertion, _ = ac.getSamlAssertion(resBodyStr); samlAssertion != "" {
+ logger.Debug("processing a SAMLResponse")
+ return samlAssertion, nil
+ }
+ logger.Debug("processing a 'hiddenform'")
+ res, err = ac.reProcessForm(resBodyStr)
+ default:
+ if strings.Contains(resBodyStr, "$Config") {
+ if err := ac.unmarshalEmbeddedJson(resBodyStr, &convergedResponse); err != nil {
+ return samlAssertion, errors.Wrap(err, "unmarshal error")
+ }
+ logger.Debug("unknown process step found:", convergedResponse.Pgid)
+ } else {
+ logger.Debug("reached an unknown page within the authentication process")
+ }
+ break AuthProcessor
+ }
+ if err != nil {
+ return samlAssertion, err
+ }
}
- // data is embedded javascript object
- //