[FEATURE] Support MySQL as a storage backend. (#678)
* [FEATURE] Support MySQL as a storage backend. Fixes #512. * Fix integration tests and include MySQL in docs.pull/684/head
parent
e033a399a7
commit
0dea0fc82e
|
@ -16,5 +16,5 @@ The available options are:
|
||||||
|
|
||||||
* [SQLite](./sqlite.md)
|
* [SQLite](./sqlite.md)
|
||||||
* [MariaDB](./mariadb.md)
|
* [MariaDB](./mariadb.md)
|
||||||
* ~~MySQL~~ ([#512](https://github.com/authelia/authelia/issues/512))
|
* [MySQL](./mysql.md)
|
||||||
* [Postgres](./postgres.md)
|
* [Postgres](./postgres.md)
|
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
layout: default
|
||||||
|
title: MySQL
|
||||||
|
parent: Storage backends
|
||||||
|
grand_parent: Configuration
|
||||||
|
nav_order: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# MySQL
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
storage:
|
||||||
|
mysql:
|
||||||
|
host: 127.0.0.1
|
||||||
|
port: 3306
|
||||||
|
database: authelia
|
||||||
|
username: authelia
|
||||||
|
# This secret can also be set using the env variables AUTHELIA_STORAGE_MYSQL_PASSWORD
|
||||||
|
password: mypassword
|
||||||
|
```
|
|
@ -3,7 +3,7 @@ layout: default
|
||||||
title: SQLite
|
title: SQLite
|
||||||
parent: Storage backends
|
parent: Storage backends
|
||||||
grand_parent: Configuration
|
grand_parent: Configuration
|
||||||
nav_order: 3
|
nav_order: 4
|
||||||
---
|
---
|
||||||
|
|
||||||
# SQLite
|
# SQLite
|
||||||
|
|
|
@ -1,8 +1,44 @@
|
||||||
package storage
|
package storage
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
// Keep table names in lower case because some DB does not support upper case.
|
// Keep table names in lower case because some DB does not support upper case.
|
||||||
var preferencesTableName = "user_preferences"
|
var preferencesTableName = "user_preferences"
|
||||||
var identityVerificationTokensTableName = "identity_verification_tokens"
|
var identityVerificationTokensTableName = "identity_verification_tokens"
|
||||||
var totpSecretsTableName = "totp_secrets"
|
var totpSecretsTableName = "totp_secrets"
|
||||||
var u2fDeviceHandlesTableName = "u2f_devices"
|
var u2fDeviceHandlesTableName = "u2f_devices"
|
||||||
var authenticationLogsTableName = "authentication_logs"
|
var authenticationLogsTableName = "authentication_logs"
|
||||||
|
|
||||||
|
// SQLCreateUserPreferencesTable common SQL query to create user_preferences table
|
||||||
|
var SQLCreateUserPreferencesTable = fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
|
username VARCHAR(100) PRIMARY KEY,
|
||||||
|
second_factor_method VARCHAR(11)
|
||||||
|
)`, preferencesTableName)
|
||||||
|
|
||||||
|
// SQLCreateIdentityVerificationTokensTable common SQL query to create identity_verification_tokens table
|
||||||
|
var SQLCreateIdentityVerificationTokensTable = fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (token VARCHAR(512))
|
||||||
|
`, identityVerificationTokensTableName)
|
||||||
|
|
||||||
|
// SQLCreateTOTPSecretsTable common SQL query to create totp_secrets table
|
||||||
|
var SQLCreateTOTPSecretsTable = fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, secret VARCHAR(64))
|
||||||
|
`, totpSecretsTableName)
|
||||||
|
|
||||||
|
// SQLCreateU2FDeviceHandlesTable common SQL query to create u2f_device_handles table
|
||||||
|
var SQLCreateU2FDeviceHandlesTable = fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
|
username VARCHAR(100) PRIMARY KEY,
|
||||||
|
keyHandle TEXT,
|
||||||
|
publicKey TEXT
|
||||||
|
)`, u2fDeviceHandlesTableName)
|
||||||
|
|
||||||
|
// SQLCreateAuthenticationLogsTable common SQL query to create authentication_logs table
|
||||||
|
var SQLCreateAuthenticationLogsTable = fmt.Sprintf(`
|
||||||
|
CREATE TABLE IF NOT EXISTS %s (
|
||||||
|
username VARCHAR(100),
|
||||||
|
successful BOOL,
|
||||||
|
time INTEGER,
|
||||||
|
INDEX usr_time_idx (username, time)
|
||||||
|
)`, authenticationLogsTableName)
|
||||||
|
|
|
@ -43,6 +43,12 @@ func NewMySQLProvider(configuration schema.MySQLStorageConfiguration) *MySQLProv
|
||||||
|
|
||||||
provider := MySQLProvider{
|
provider := MySQLProvider{
|
||||||
SQLProvider{
|
SQLProvider{
|
||||||
|
sqlCreateUserPreferencesTable: SQLCreateUserPreferencesTable,
|
||||||
|
sqlCreateIdentityVerificationTokensTable: SQLCreateIdentityVerificationTokensTable,
|
||||||
|
sqlCreateTOTPSecretsTable: SQLCreateTOTPSecretsTable,
|
||||||
|
sqlCreateU2FDeviceHandlesTable: SQLCreateU2FDeviceHandlesTable,
|
||||||
|
sqlCreateAuthenticationLogsTable: SQLCreateAuthenticationLogsTable,
|
||||||
|
|
||||||
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=?", preferencesTableName),
|
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=?", preferencesTableName),
|
||||||
sqlUpsertSecondFactorPreference: fmt.Sprintf("REPLACE INTO %s (username, second_factor_method) VALUES (?, ?)", preferencesTableName),
|
sqlUpsertSecondFactorPreference: fmt.Sprintf("REPLACE INTO %s (username, second_factor_method) VALUES (?, ?)", preferencesTableName),
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,13 @@ func NewPostgreSQLProvider(configuration schema.PostgreSQLStorageConfiguration)
|
||||||
|
|
||||||
provider := PostgreSQLProvider{
|
provider := PostgreSQLProvider{
|
||||||
SQLProvider{
|
SQLProvider{
|
||||||
|
sqlCreateUserPreferencesTable: SQLCreateUserPreferencesTable,
|
||||||
|
sqlCreateIdentityVerificationTokensTable: SQLCreateIdentityVerificationTokensTable,
|
||||||
|
sqlCreateTOTPSecretsTable: SQLCreateTOTPSecretsTable,
|
||||||
|
sqlCreateU2FDeviceHandlesTable: SQLCreateU2FDeviceHandlesTable,
|
||||||
|
sqlCreateAuthenticationLogsTable: fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100), successful BOOL, time INTEGER)", authenticationLogsTableName),
|
||||||
|
sqlCreateAuthenticationLogsUserTimeIndex: fmt.Sprintf("CREATE INDEX IF NOT EXISTS usr_time_idx ON %s (username, time)", authenticationLogsTableName),
|
||||||
|
|
||||||
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=$1", preferencesTableName),
|
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=$1", preferencesTableName),
|
||||||
sqlUpsertSecondFactorPreference: fmt.Sprintf("INSERT INTO %s (username, second_factor_method) VALUES ($1, $2) ON CONFLICT (username) DO UPDATE SET second_factor_method=$2", preferencesTableName),
|
sqlUpsertSecondFactorPreference: fmt.Sprintf("INSERT INTO %s (username, second_factor_method) VALUES ($1, $2) ON CONFLICT (username) DO UPDATE SET second_factor_method=$2", preferencesTableName),
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,13 @@ import (
|
||||||
type SQLProvider struct {
|
type SQLProvider struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
|
|
||||||
|
sqlCreateUserPreferencesTable string
|
||||||
|
sqlCreateIdentityVerificationTokensTable string
|
||||||
|
sqlCreateTOTPSecretsTable string
|
||||||
|
sqlCreateU2FDeviceHandlesTable string
|
||||||
|
sqlCreateAuthenticationLogsTable string
|
||||||
|
sqlCreateAuthenticationLogsUserTimeIndex string
|
||||||
|
|
||||||
sqlGetPreferencesByUsername string
|
sqlGetPreferencesByUsername string
|
||||||
sqlUpsertSecondFactorPreference string
|
sqlUpsertSecondFactorPreference string
|
||||||
|
|
||||||
|
@ -34,40 +41,39 @@ type SQLProvider struct {
|
||||||
func (p *SQLProvider) initialize(db *sql.DB) error {
|
func (p *SQLProvider) initialize(db *sql.DB) error {
|
||||||
p.db = db
|
p.db = db
|
||||||
|
|
||||||
_, err := db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, second_factor_method VARCHAR(11))", preferencesTableName))
|
_, err := db.Exec(p.sqlCreateUserPreferencesTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", preferencesTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (token VARCHAR(512))", identityVerificationTokensTableName))
|
_, err = db.Exec(p.sqlCreateIdentityVerificationTokensTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", identityVerificationTokensTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, secret VARCHAR(64))", totpSecretsTableName))
|
_, err = db.Exec(p.sqlCreateTOTPSecretsTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", totpSecretsTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// keyHandle and publicKey are stored in base64 format
|
// keyHandle and publicKey are stored in base64 format
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100) PRIMARY KEY, keyHandle TEXT, publicKey TEXT)", u2fDeviceHandlesTableName))
|
_, err = db.Exec(p.sqlCreateU2FDeviceHandlesTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", u2fDeviceHandlesTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100), successful BOOL, time INTEGER)", authenticationLogsTableName))
|
_, err = db.Exec(p.sqlCreateAuthenticationLogsTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", authenticationLogsTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE INDEX IF NOT EXISTS time ON %s (time);", authenticationLogsTableName))
|
// Create an index on (username, time) because this couple is highly used by the regulation module
|
||||||
|
// to check whether a user is banned.
|
||||||
|
if p.sqlCreateAuthenticationLogsUserTimeIndex != "" {
|
||||||
|
_, err = db.Exec(p.sqlCreateAuthenticationLogsUserTimeIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Unable to create table %s: %v", authenticationLogsTableName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = db.Exec(fmt.Sprintf("CREATE INDEX IF NOT EXISTS username ON %s (username);", authenticationLogsTableName))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,13 @@ func NewSQLiteProvider(path string) *SQLiteProvider {
|
||||||
|
|
||||||
provider := SQLiteProvider{
|
provider := SQLiteProvider{
|
||||||
SQLProvider{
|
SQLProvider{
|
||||||
|
sqlCreateUserPreferencesTable: SQLCreateUserPreferencesTable,
|
||||||
|
sqlCreateIdentityVerificationTokensTable: SQLCreateIdentityVerificationTokensTable,
|
||||||
|
sqlCreateTOTPSecretsTable: SQLCreateTOTPSecretsTable,
|
||||||
|
sqlCreateU2FDeviceHandlesTable: SQLCreateU2FDeviceHandlesTable,
|
||||||
|
sqlCreateAuthenticationLogsTable: fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (username VARCHAR(100), successful BOOL, time INTEGER)", authenticationLogsTableName),
|
||||||
|
sqlCreateAuthenticationLogsUserTimeIndex: fmt.Sprintf("CREATE INDEX IF NOT EXISTS usr_time_idx ON %s (username, time)", authenticationLogsTableName),
|
||||||
|
|
||||||
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=?", preferencesTableName),
|
sqlGetPreferencesByUsername: fmt.Sprintf("SELECT second_factor_method FROM %s WHERE username=?", preferencesTableName),
|
||||||
sqlUpsertSecondFactorPreference: fmt.Sprintf("REPLACE INTO %s (username, second_factor_method) VALUES (?, ?)", preferencesTableName),
|
sqlUpsertSecondFactorPreference: fmt.Sprintf("REPLACE INTO %s (username, second_factor_method) VALUES (?, ?)", preferencesTableName),
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
###############################################################
|
||||||
|
# Authelia minimal configuration #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
port: 9091
|
||||||
|
|
||||||
|
logs_level: debug
|
||||||
|
|
||||||
|
default_redirection_url: https://home.example.com:8080/
|
||||||
|
|
||||||
|
jwt_secret: very_important_secret
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
file:
|
||||||
|
path: /var/lib/authelia/users.yml
|
||||||
|
|
||||||
|
session:
|
||||||
|
secret: unsecure_session_secret
|
||||||
|
domain: example.com
|
||||||
|
expiration: 3600 # 1 hour
|
||||||
|
inactivity: 300 # 5 minutes
|
||||||
|
|
||||||
|
# Configuration of the storage backend used to store data and secrets. i.e. totp data
|
||||||
|
storage:
|
||||||
|
mysql:
|
||||||
|
host: mysql
|
||||||
|
port: 3306
|
||||||
|
database: authelia
|
||||||
|
username: admin
|
||||||
|
password: password
|
||||||
|
|
||||||
|
# TOTP Issuer Name
|
||||||
|
#
|
||||||
|
# This will be the issuer name displayed in Google Authenticator
|
||||||
|
# See: https://github.com/google/google-authenticator/wiki/Key-Uri-Format for more info on issuer names
|
||||||
|
totp:
|
||||||
|
issuer: example.com
|
||||||
|
|
||||||
|
access_control:
|
||||||
|
default_policy: deny
|
||||||
|
rules:
|
||||||
|
- domain: "public.example.com"
|
||||||
|
policy: bypass
|
||||||
|
- domain: "admin.example.com"
|
||||||
|
policy: two_factor
|
||||||
|
- domain: "secure.example.com"
|
||||||
|
policy: two_factor
|
||||||
|
- domain: "singlefactor.example.com"
|
||||||
|
policy: one_factor
|
||||||
|
|
||||||
|
# Configuration of the authentication regulation mechanism.
|
||||||
|
regulation:
|
||||||
|
# Set it to 0 to disable max_retries.
|
||||||
|
max_retries: 3
|
||||||
|
|
||||||
|
# The user is banned if the authentication failed `max_retries` times in a `find_time` seconds window.
|
||||||
|
find_time: 8
|
||||||
|
|
||||||
|
# The length of time before a banned user can login again.
|
||||||
|
ban_time: 10
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
# Use a SMTP server for sending notifications
|
||||||
|
smtp:
|
||||||
|
host: smtp
|
||||||
|
port: 1025
|
||||||
|
sender: admin@example.com
|
||||||
|
disable_require_tls: true
|
|
@ -0,0 +1,6 @@
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
authelia-backend:
|
||||||
|
volumes:
|
||||||
|
- './MySQL/configuration.yml:/etc/authelia/configuration.yml:ro'
|
||||||
|
- './MySQL/users.yml:/var/lib/authelia/users.yml'
|
|
@ -0,0 +1,29 @@
|
||||||
|
###############################################################
|
||||||
|
# Users Database #
|
||||||
|
###############################################################
|
||||||
|
|
||||||
|
# This file can be used if you do not have an LDAP set up.
|
||||||
|
|
||||||
|
# List of users
|
||||||
|
users:
|
||||||
|
john:
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: john.doe@authelia.com
|
||||||
|
groups:
|
||||||
|
- admins
|
||||||
|
- dev
|
||||||
|
|
||||||
|
harry:
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: harry.potter@authelia.com
|
||||||
|
groups: []
|
||||||
|
|
||||||
|
bob:
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: bob.dylan@authelia.com
|
||||||
|
groups:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
james:
|
||||||
|
password: "$6$rounds=500000$jgiCMRyGXzoqpxS3$w2pJeZnnH8bwW3zzvoMWtTRfQYsHbWbD/hquuQ5vUeIyl9gdwBIt6RWk2S6afBA0DPakbeWgD/4SZPiS0hYtU/"
|
||||||
|
email: james.dean@authelia.com
|
|
@ -0,0 +1,11 @@
|
||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.0
|
||||||
|
environment:
|
||||||
|
- MYSQL_ROOT_PASSWORD=rootpassword
|
||||||
|
- MYSQL_USER=admin
|
||||||
|
- MYSQL_PASSWORD=password
|
||||||
|
- MYSQL_DATABASE=authelia
|
||||||
|
networks:
|
||||||
|
- authelianet
|
|
@ -0,0 +1,58 @@
|
||||||
|
package suites
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var mysqlSuiteName = "MySQL"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
dockerEnvironment := NewDockerEnvironment([]string{
|
||||||
|
"internal/suites/docker-compose.yml",
|
||||||
|
"internal/suites/MySQL/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/authelia/docker-compose.backend.{}.yml",
|
||||||
|
"internal/suites/example/compose/authelia/docker-compose.frontend.{}.yml",
|
||||||
|
"internal/suites/example/compose/nginx/backend/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/nginx/portal/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/smtp/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/mysql/docker-compose.yml",
|
||||||
|
"internal/suites/example/compose/ldap/docker-compose.yml",
|
||||||
|
})
|
||||||
|
|
||||||
|
setup := func(suitePath string) error {
|
||||||
|
if err := dockerEnvironment.Up(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return waitUntilAutheliaBackendIsReady(dockerEnvironment)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSetupTimeout := func() error {
|
||||||
|
backendLogs, err := dockerEnvironment.Logs("authelia-backend", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(backendLogs)
|
||||||
|
|
||||||
|
frontendLogs, err := dockerEnvironment.Logs("authelia-frontend", nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(frontendLogs)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
teardown := func(suitePath string) error {
|
||||||
|
err := dockerEnvironment.Down()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalRegistry.Register(mysqlSuiteName, Suite{
|
||||||
|
SetUp: setup,
|
||||||
|
SetUpTimeout: 5 * time.Minute,
|
||||||
|
OnSetupTimeout: onSetupTimeout,
|
||||||
|
TearDown: teardown,
|
||||||
|
TearDownTimeout: 2 * time.Minute,
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package suites
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MySQLSuite struct {
|
||||||
|
*SeleniumSuite
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMySQLSuite() *MySQLSuite {
|
||||||
|
return &MySQLSuite{SeleniumSuite: new(SeleniumSuite)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MySQLSuite) TestOneFactorScenario() {
|
||||||
|
suite.Run(s.T(), NewOneFactorScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MySQLSuite) TestTwoFactorScenario() {
|
||||||
|
suite.Run(s.T(), NewTwoFactorScenario())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMySQLSuite(t *testing.T) {
|
||||||
|
suite.Run(t, NewMySQLSuite())
|
||||||
|
}
|
|
@ -27,8 +27,13 @@ func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
browserPath := os.Getenv("BROWSER_PATH")
|
||||||
|
if browserPath == "" {
|
||||||
|
browserPath = "/usr/bin/chromium-browser"
|
||||||
|
}
|
||||||
|
|
||||||
chromeCaps := chrome.Capabilities{
|
chromeCaps := chrome.Capabilities{
|
||||||
Path: "/usr/bin/chromium-browser",
|
Path: browserPath,
|
||||||
}
|
}
|
||||||
|
|
||||||
chromeCaps.Args = append(chromeCaps.Args, "--ignore-certificate-errors")
|
chromeCaps.Args = append(chromeCaps.Args, "--ignore-certificate-errors")
|
||||||
|
|
Loading…
Reference in New Issue