mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
8.5 KiB
384 lines
8.5 KiB
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, |
|
|
|
logged_in boolean not null, |
|
|
|
claims_user_id text not null, |
|
claims_username text not null, |
|
claims_email text not null, |
|
claims_email_verified boolean not null, |
|
claims_groups bytea not null, -- JSON array of strings |
|
|
|
connector_id text not null, |
|
connector_data bytea, |
|
|
|
expiry timestamptz not null |
|
);`, |
|
` |
|
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, |
|
|
|
claims_user_id text not null, |
|
claims_username text not null, |
|
claims_email text not null, |
|
claims_email_verified boolean not null, |
|
claims_groups bytea not null, -- JSON array of strings |
|
|
|
connector_id text not null, |
|
connector_data bytea, |
|
|
|
expiry timestamptz not null |
|
);`, |
|
` |
|
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, |
|
|
|
claims_user_id text not null, |
|
claims_username text not null, |
|
claims_email text not null, |
|
claims_email_verified boolean not null, |
|
claims_groups bytea not null, -- JSON array of strings |
|
|
|
connector_id text not null, |
|
connector_data bytea |
|
);`, |
|
` |
|
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 |
|
);`, |
|
}, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
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 '';`, |
|
}, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table auth_request |
|
add column hmac_key bytea;`, |
|
}, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column preferred_username text not null default '';`, |
|
` |
|
alter table password |
|
add column groups bytea not null default convert_to('[]', 'UTF8');`, |
|
}, |
|
flavor: &flavorPostgres, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column preferred_username text not null default '';`, |
|
` |
|
alter table password |
|
add column groups bytea not null default '[]';`, |
|
}, |
|
flavor: &flavorSQLite3, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column preferred_username text not null default '';`, |
|
` |
|
alter table password |
|
add column groups bytea;`, |
|
` |
|
update password |
|
set groups = '[]' |
|
where groups is null;`, |
|
` |
|
alter table password |
|
modify column groups bytea not null;`, |
|
}, |
|
flavor: &flavorMySQL, |
|
}, |
|
// Migration for adding name and email_verified to password table (Postgres) |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column name text not null default '';`, |
|
` |
|
alter table password |
|
add column email_verified boolean;`, |
|
}, |
|
flavor: &flavorPostgres, |
|
}, |
|
// Migration for adding name and email_verified to password table (SQLite3) |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column name text not null default '';`, |
|
` |
|
alter table password |
|
add column email_verified boolean;`, |
|
}, |
|
flavor: &flavorSQLite3, |
|
}, |
|
// Migration for adding name and email_verified to password table (MySQL) |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table password |
|
add column name text not null default '';`, |
|
` |
|
alter table password |
|
add column email_verified boolean;`, |
|
}, |
|
flavor: &flavorMySQL, |
|
}, |
|
{ |
|
stmts: []string{ |
|
` |
|
alter table connector |
|
add column grant_types bytea;`, |
|
}, |
|
}, |
|
}
|
|
|