dex/storage/sql/migrate.go

295 lines
6.7 KiB
Go
Raw Normal View History

package sql
import (
"database/sql"
"fmt"
)
func (c *conn) migrate() (int, error) {
_, err := c.Exec(`
create table if not exists migrations (
num integer not null,
at timestamptz not null
);
`)
if err != nil {
return 0, fmt.Errorf("creating migration table: %v", err)
}
i := 0
done := false
var flavorMigrations []migration
for _, m := range migrations {
if m.flavor == nil || m.flavor == c.flavor {
flavorMigrations = append(flavorMigrations, m)
}
}
for {
err := c.ExecTx(func(tx *trans) error {
// Within a transaction, perform a single migration.
var (
num sql.NullInt64
n int
)
if err := tx.QueryRow(`select max(num) from migrations;`).Scan(&num); err != nil {
return fmt.Errorf("select max migration: %v", err)
}
if num.Valid {
n = int(num.Int64)
}
if n >= len(flavorMigrations) {
done = true
return nil
}
migrationNum := n + 1
m := flavorMigrations[n]
for i := range m.stmts {
if _, err := tx.Exec(m.stmts[i]); err != nil {
return fmt.Errorf("migration %d statement %d failed: %v", migrationNum, i+1, err)
}
}
q := `insert into migrations (num, at) values ($1, now());`
if _, err := tx.Exec(q, migrationNum); err != nil {
return fmt.Errorf("update migration table: %v", err)
}
return nil
})
if err != nil {
return i, err
}
if done {
break
}
i++
}
return i, nil
}
type migration struct {
stmts []string
// If flavor is nil the migration will take place for all database backend flavors.
// If specified, only for that corresponding flavor, in that case stmts can be written
// in the specific SQL dialect.
flavor *flavor
}
// All SQL flavors share migration strategies.
var migrations = []migration{
{
stmts: []string{
`
create table client (
id text not null primary key,
secret text not null,
redirect_uris bytea not null, -- JSON array of strings
trusted_peers bytea not null, -- JSON array of strings
public boolean not null,
name text not null,
logo_url text not null
);`,
`
create table auth_request (
id text not null primary key,
client_id text not null,
response_types bytea not null, -- JSON array of strings
scopes bytea not null, -- JSON array of strings
redirect_uri text not null,
nonce text not null,
state text not null,
force_approval_prompt boolean not null,
2018-01-30 16:49:08 +05:30
logged_in boolean not null,
2018-01-30 16:49:08 +05:30
claims_user_id text not null,
claims_username text not null,
claims_email text not null,
claims_email_verified boolean not null,
claims_groups bytea not null, -- JSON array of strings
2018-01-30 16:49:08 +05:30
connector_id text not null,
connector_data bytea,
2018-01-30 16:49:08 +05:30
expiry timestamptz not null
);`,
`
create table auth_code (
id text not null primary key,
client_id text not null,
scopes bytea not null, -- JSON array of strings
nonce text not null,
redirect_uri text not null,
2018-01-30 16:49:08 +05:30
claims_user_id text not null,
claims_username text not null,
claims_email text not null,
claims_email_verified boolean not null,
claims_groups bytea not null, -- JSON array of strings
2018-01-30 16:49:08 +05:30
connector_id text not null,
connector_data bytea,
2018-01-30 16:49:08 +05:30
expiry timestamptz not null
);`,
`
create table refresh_token (
id text not null primary key,
client_id text not null,
scopes bytea not null, -- JSON array of strings
nonce text not null,
2018-01-30 16:49:08 +05:30
claims_user_id text not null,
claims_username text not null,
claims_email text not null,
claims_email_verified boolean not null,
claims_groups bytea not null, -- JSON array of strings
2018-01-30 16:49:08 +05:30
connector_id text not null,
connector_data bytea
);`,
`
2016-10-06 04:34:11 +05:30
create table password (
email text not null primary key,
hash bytea not null,
username text not null,
user_id text not null
);`,
`
-- keys is a weird table because we only ever expect there to be a single row
create table keys (
id text not null primary key,
verification_keys bytea not null, -- JSON array
signing_key bytea not null, -- JSON object
signing_key_pub bytea not null, -- JSON object
next_rotation timestamptz not null
);`,
},
},
{
stmts: []string{
`
alter table refresh_token
add column token text not null default '';`,
`
alter table refresh_token
add column created_at timestamptz not null default '0001-01-01 00:00:00 UTC';`,
`
alter table refresh_token
add column last_used timestamptz not null default '0001-01-01 00:00:00 UTC';`,
},
},
{
stmts: []string{
`
create table offline_session (
user_id text not null,
conn_id text not null,
refresh bytea not null,
PRIMARY KEY (user_id, conn_id)
);`,
},
},
{
stmts: []string{
`
create table connector (
id text not null primary key,
type text not null,
name text not null,
resource_version text not null,
config bytea
);`,
},
},
{
stmts: []string{
`
alter table auth_code
add column claims_preferred_username text not null default '';`,
`
alter table auth_request
add column claims_preferred_username text not null default '';`,
`
alter table refresh_token
add column claims_preferred_username text not null default '';`,
},
},
{
stmts: []string{
`
alter table offline_session
add column connector_data bytea;
`,
},
},
{
stmts: []string{
`
alter table auth_request
modify column state varchar(4096);
`,
},
flavor: &flavorMySQL,
},
{
stmts: []string{
`
create table device_request (
user_code text not null primary key,
device_code text not null,
client_id text not null,
client_secret text ,
scopes bytea not null, -- JSON array of strings
expiry timestamptz not null
);`,
`
create table device_token (
device_code text not null primary key,
status text not null,
token bytea,
expiry timestamptz not null,
last_request timestamptz not null,
poll_interval integer not null
);`,
},
},
PKCE implementation (#1784) * Basic implementation of PKCE Signed-off-by: Tadeusz Magura-Witkowski <tadeuszmw@gmail.com> * @mfmarche on 24 Feb: when code_verifier is set, don't check client_secret In PKCE flow, no client_secret is used, so the check for a valid client_secret would always fail. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * @deric on 16 Jun: return invalid_grant when wrong code_verifier Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Enforce PKCE flow on /token when PKCE flow was started on /auth Also dissallow PKCE on /token, when PKCE flow was not started on /auth Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * fixed error messages when mixed PKCE/no PKCE flow. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * server_test.go: Added PKCE error cases on /token endpoint * Added test for invalid_grant, when wrong code_verifier is sent * Added test for mixed PKCE / no PKCE auth flows. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * cleanup: extracted method checkErrorResponse and type TestDefinition * fixed connector being overwritten Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * /token endpoint: skip client_secret verification only for grand type authorization_code with PKCE extension Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Allow "Authorization" header in CORS handlers * Adds "Authorization" to the default CORS headers{"Accept", "Accept-Language", "Content-Language", "Origin"} Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Add "code_challenge_methods_supported" to discovery endpoint discovery endpoint /dex/.well-known/openid-configuration now has the following entry: "code_challenge_methods_supported": [ "S256", "plain" ] Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Updated tests (mixed-up comments), added a PKCE test * @asoorm added test that checks if downgrade to "plain" on /token endpoint Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * remove redefinition of providedCodeVerifier, fixed spelling (#6) Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> Signed-off-by: Bernd Eckstein <HEllRZA@users.noreply.github.com> * Rename struct CodeChallenge to PKCE Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * PKCE: Check clientSecret when available In authorization_code flow with PKCE, allow empty client_secret on /auth and /token endpoints. But check the client_secret when it is given. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Enable PKCE with public: true dex configuration public on staticClients now enables the following behavior in PKCE: - Public: false, PKCE will always check client_secret. This means PKCE in it's natural form is disabled. - Public: true, PKCE is enabled. It will only check client_secret if the client has sent one. But it allows the code flow if the client didn't sent one. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Redirect error on unsupported code_challenge_method - Check for unsupported code_challenge_method after redirect uri is validated, and use newErr() to return the error. - Add PKCE tests to oauth2_test.go Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Reverted go.mod and go.sum to the state of master Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Don't omit client secret check for PKCE Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Allow public clients (e.g. with PKCE) to have redirect URIs configured Signed-off-by: Martin Heide <martin.heide@faro.com> * Remove "Authorization" as Accepted Headers on CORS, small fixes Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Revert "Allow public clients (e.g. with PKCE) to have redirect URIs configured" This reverts commit b6e297b78537dc44cd3e1374f0b4d34bf89404ac. Signed-off-by: Martin Heide <martin.heide@faro.com> * PKCE on client_secret client error message * When connecting to the token endpoint with PKCE without client_secret, but the client is configured with a client_secret, generate a special error message. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Output info message when PKCE without client_secret used on confidential client * removes the special error message Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * General missing/invalid client_secret message on token endpoint Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> Co-authored-by: Tadeusz Magura-Witkowski <tadeuszmw@gmail.com> Co-authored-by: Martin Heide <martin.heide@faro.com> Co-authored-by: M. Heide <66078329+heidemn-faro@users.noreply.github.com>
2020-10-26 16:03:40 +05:30
{
stmts: []string{
`
PKCE implementation (#1784) * Basic implementation of PKCE Signed-off-by: Tadeusz Magura-Witkowski <tadeuszmw@gmail.com> * @mfmarche on 24 Feb: when code_verifier is set, don't check client_secret In PKCE flow, no client_secret is used, so the check for a valid client_secret would always fail. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * @deric on 16 Jun: return invalid_grant when wrong code_verifier Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Enforce PKCE flow on /token when PKCE flow was started on /auth Also dissallow PKCE on /token, when PKCE flow was not started on /auth Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * fixed error messages when mixed PKCE/no PKCE flow. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * server_test.go: Added PKCE error cases on /token endpoint * Added test for invalid_grant, when wrong code_verifier is sent * Added test for mixed PKCE / no PKCE auth flows. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * cleanup: extracted method checkErrorResponse and type TestDefinition * fixed connector being overwritten Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * /token endpoint: skip client_secret verification only for grand type authorization_code with PKCE extension Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Allow "Authorization" header in CORS handlers * Adds "Authorization" to the default CORS headers{"Accept", "Accept-Language", "Content-Language", "Origin"} Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Add "code_challenge_methods_supported" to discovery endpoint discovery endpoint /dex/.well-known/openid-configuration now has the following entry: "code_challenge_methods_supported": [ "S256", "plain" ] Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Updated tests (mixed-up comments), added a PKCE test * @asoorm added test that checks if downgrade to "plain" on /token endpoint Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * remove redefinition of providedCodeVerifier, fixed spelling (#6) Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> Signed-off-by: Bernd Eckstein <HEllRZA@users.noreply.github.com> * Rename struct CodeChallenge to PKCE Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * PKCE: Check clientSecret when available In authorization_code flow with PKCE, allow empty client_secret on /auth and /token endpoints. But check the client_secret when it is given. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Enable PKCE with public: true dex configuration public on staticClients now enables the following behavior in PKCE: - Public: false, PKCE will always check client_secret. This means PKCE in it's natural form is disabled. - Public: true, PKCE is enabled. It will only check client_secret if the client has sent one. But it allows the code flow if the client didn't sent one. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Redirect error on unsupported code_challenge_method - Check for unsupported code_challenge_method after redirect uri is validated, and use newErr() to return the error. - Add PKCE tests to oauth2_test.go Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Reverted go.mod and go.sum to the state of master Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Don't omit client secret check for PKCE Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Allow public clients (e.g. with PKCE) to have redirect URIs configured Signed-off-by: Martin Heide <martin.heide@faro.com> * Remove "Authorization" as Accepted Headers on CORS, small fixes Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Revert "Allow public clients (e.g. with PKCE) to have redirect URIs configured" This reverts commit b6e297b78537dc44cd3e1374f0b4d34bf89404ac. Signed-off-by: Martin Heide <martin.heide@faro.com> * PKCE on client_secret client error message * When connecting to the token endpoint with PKCE without client_secret, but the client is configured with a client_secret, generate a special error message. Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * Output info message when PKCE without client_secret used on confidential client * removes the special error message Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> * General missing/invalid client_secret message on token endpoint Signed-off-by: Bernd Eckstein <Bernd.Eckstein@faro.com> Co-authored-by: Tadeusz Magura-Witkowski <tadeuszmw@gmail.com> Co-authored-by: Martin Heide <martin.heide@faro.com> Co-authored-by: M. Heide <66078329+heidemn-faro@users.noreply.github.com>
2020-10-26 16:03:40 +05:30
alter table auth_request
add column code_challenge text not null default '';`,
`
alter table auth_request
add column code_challenge_method text not null default '';`,
`
alter table auth_code
add column code_challenge text not null default '';`,
`
alter table auth_code
add column code_challenge_method text not null default '';`,
},
},
{
stmts: []string{
`
alter table refresh_token
add column obsolete_token text default '';`,
},
},
{
stmts: []string{
`
alter table device_token
add column code_challenge text not null default '';`,
`
alter table device_token
add column code_challenge_method text not null default '';`,
},
},
}