# Verify system-identity to database-user substitutions.

config secure
----


# Set HBA to add an ident map.
set_hba
host  all all  all cert-password map=testing
----
# Active authentication configuration on this node:
# Original configuration:
# loopback all all all trust       # built-in CockroachDB default
# host  all root all cert-password # CockroachDB mandatory rule
# host  all all  all cert-password map=testing
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
loopback all      all  all     trust
host     all      root all     cert-password
host     all      all  all     cert-password map=testing

set_identity_map
testing testuser carl               # Exact remapping
testing /(.*)@cockroachlabs.com \1  # Generalized domain mapping
testing testuser another_carl       # Another candidate mapping
testing will_be_carl carl           # Another user for password testing
testing testuser2 carl              # Cert that doesn't correspond to a db user
testing testuser@example.com carl   # Cert with a non-SQL principal baked in
----
# Active authentication configuration on this node:
# Original configuration:
# loopback all all all trust       # built-in CockroachDB default
# host  all root all cert-password # CockroachDB mandatory rule
# host  all all  all cert-password map=testing
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
loopback all      all  all     trust
host     all      root all     cert-password
host     all      all  all     cert-password map=testing
# Active identity mapping on this node:
# Original configuration:
# testing testuser carl               # Exact remapping
# testing /(.*)@cockroachlabs.com \1  # Generalized domain mapping
# testing testuser another_carl       # Another candidate mapping
# testing will_be_carl carl           # Another user for password testing
# testing testuser2 carl              # Cert that doesn't correspond to a db user
# testing testuser@example.com carl   # Cert with a non-SQL principal baked in
# Active configuration:
# map-name system-username         database-username
testing    ^testuser$              carl
testing    (.*)@cockroachlabs.com  \1                # substituteAt=0
testing    ^testuser$              another_carl
testing    ^will_be_carl$          carl
testing    ^testuser2$             carl
testing    ^testuser@example\.com$ carl

sql
CREATE USER carl WITH PASSWORD 'doggo';
CREATE USER will_be_carl WITH PASSWORD 'oggod';
----
ok


subtest password_still_works_with_db_username

# Sanity-check the database user
connect user=carl database=mydb password=doggo
----
ok mydb

authlog 8
.*client_connection_end
----
2 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
3 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
4 {"EventType":"client_authentication_info","Info":"client did not present TLS certificate","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
6 {"EventType":"client_authentication_info","Info":"no crdb-bcrypt credentials found; proceeding with SCRAM-SHA-256","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
7 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
8 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
9 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
10 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end


# This test verifies that we validate the password of an incoming
# username-based request before any remapping occurs.
subtest password_evaluated_before_remapping

connect user=carl@cockroachlabs.com database=mydb password=doggo
----
ERROR: password authentication failed for user carl@cockroachlabs.com (SQLSTATE 28P01)

# Since we're evaluating before remapping, the password extraction will fail
# for user carl@cockroachlabs.com and we don't even get information about
# which protocol is used in logs.
authlog 6
.*client_connection_end
----
11 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
12 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl@cockroachlabs.com","Timestamp":"XXX","Transport":"hostssl"}
13 {"EventType":"client_authentication_info","Info":"client did not present TLS certificate","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl@cockroachlabs.com","Timestamp":"XXX","Transport":"hostssl"}
14 {"EventType":"client_authentication_failed","InstanceID":1,"Method":"cert-password","Network":"tcp","Reason":"USER_NOT_FOUND","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl@cockroachlabs.com","Timestamp":"XXX","Transport":"hostssl","User":"carl@cockroachlabs.com"}
15 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
16 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# Verify the good path, where we verify the password against the
# system identity and get a remapping.
subtest password_remapped_user_ok

connect user=will_be_carl database=mydb password=oggod show_system_identity
----
ok mydb will_be_carl

authlog 8
.*client_connection_end
----
17 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
18 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"will_be_carl","Timestamp":"XXX","Transport":"hostssl"}
19 {"EventType":"client_authentication_info","Info":"client did not present TLS certificate","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"will_be_carl","Timestamp":"XXX","Transport":"hostssl"}
21 {"EventType":"client_authentication_info","Info":"no crdb-bcrypt credentials found; proceeding with SCRAM-SHA-256","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"will_be_carl","Timestamp":"XXX","Transport":"hostssl","User":"will_be_carl"}
22 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"will_be_carl","Timestamp":"XXX","Transport":"hostssl","User":"will_be_carl"}
23 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"will_be_carl","Timestamp":"XXX","Transport":"hostssl","User":"will_be_carl"}
24 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
25 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# Connect as the magic "testuser" since that comes pre-equipped with a cert.
subtest certificate_good

connect user=carl database=mydb system_identity=testuser force_certs show_system_identity
----
ok mydb testuser

authlog 7
.*client_connection_end
----
26 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
27 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl"}
28 {"EventType":"client_authentication_info","Info":"client presented certificate, proceeding with certificate validation","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl"}
30 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
31 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
32 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
33 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# There's a certificate on disk for a "testuser2" principal that doesn't
# correspond to an actual SQL user. We want to test the case where
# arbitrary system identities in a certificate must be mapped onto a
# database username.
subtest cert_with_principal_not_in_users

connect system_identity=testuser2 user=carl database=mydb force_certs show_system_identity
----
ok mydb testuser2

authlog 7
.*client_connection_end
----
34 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
35 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser2","Timestamp":"XXX","Transport":"hostssl"}
36 {"EventType":"client_authentication_info","Info":"client presented certificate, proceeding with certificate validation","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser2","Timestamp":"XXX","Transport":"hostssl"}
38 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser2","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
39 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser2","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
40 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
41 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# Connect with carl as dbuser and pass testuser as cert to be used, dont pass explicit system-identity, so that the
# system-identity is picked from the cert by auth method.
subtest certificate_with_user_mapping_no_explicit_system_identity

connect user=carl database=mydb cert_name=testuser_cn_only force_certs show_system_identity
----
ok mydb testuser

authlog 7
.*client_connection_end
----
42 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
43 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
44 {"EventType":"client_authentication_info","Info":"client presented certificate, proceeding with certificate validation","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
46 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
47 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
48 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
49 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# Connect with carl as dbuser and pass testuser as cert to be used, dont pass explicit system-identity, so that the
# system-identity is picked from the cert by auth method.
subtest cn_and_san_certificate_with_user_mapping

connect user=carl database=mydb cert_name=testuser_cn_and_san force_certs show_system_identity
----
ok mydb testuser

authlog 7
.*client_connection_end
----
50 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
51 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
52 {"EventType":"client_authentication_info","Info":"client presented certificate, proceeding with certificate validation","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
54 {"EventType":"client_authentication_ok","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
55 {"EventType":"client_authentication_info","Info":"session created with SessionDefaults=[database=mydb] and CustomOptions=[]","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"testuser","Timestamp":"XXX","Transport":"hostssl","User":"carl"}
56 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
57 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

# Connect with carl as dbuser and pass testuser as cert to be used, dont pass explicit system-identity, so that the
# system-identity is picked from the cert by auth method.
subtest san_only_certificate_with_user_mapping

connect user=carl database=mydb cert_name=testuser_san_only force_certs show_system_identity
----
ERROR: system identity "" did not map to a database role (SQLSTATE 28000)

authlog 6
.*client_connection_end
----
58 {"EventType":"client_connection_start","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
59 {"EventType":"client_authentication_info","Info":"HBA rule: host  all all  all cert-password map=testing","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
60 {"EventType":"client_authentication_info","Info":"client presented certificate, proceeding with certificate validation","InstanceID":1,"Method":"cert-password","Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","SystemIdentity":"carl","Timestamp":"XXX","Transport":"hostssl"}
61 {"Detail":"system identity \"\" did not map to a database role","EventType":"client_authentication_failed","InstanceID":1,"Method":"cert-password","Network":"tcp","Reason":"USER_NOT_FOUND","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX","Transport":"hostssl"}
62 {"Duration":"NNN","EventType":"client_session_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}
63 {"Duration":"NNN","EventType":"client_connection_end","InstanceID":1,"Network":"tcp","RemoteAddress":"XXX","SessionID":"XXX","Timestamp":"XXX"}

subtest end

subtest password_should_not_accept_map

set_hba
host   all      all  all     password map=testing
----
ERROR: the HBA method "password" does not accept options

subtest end

subtest trust_should_not_accept_map

set_hba
host   all      all  all     trust map=testing
----
ERROR: the HBA method "trust" does not accept options

subtest end

subtest verify_root_mapping_fails

set_identity_map
testing testuser root               # Exact remapping
----
# Active authentication configuration on this node:
# Original configuration:
# loopback all all all trust       # built-in CockroachDB default
# host  all root all cert-password # CockroachDB mandatory rule
# host  all all  all cert-password map=testing
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
loopback all      all  all     trust
host     all      root all     cert-password
host     all      all  all     cert-password map=testing
# Active identity mapping on this node:
# Original configuration:
# testing testuser root               # Exact remapping
# Active configuration:
# map-name system-username database-username
testing    ^testuser$      root

connect user=testuser database=mydb
----
ERROR: system identity "testuser" mapped to reserved database role "root" (SQLSTATE 28000)

subtest end

subtest verify_node_mapping_fails

set_identity_map
testing testuser node               # Exact remapping
----
# Active authentication configuration on this node:
# Original configuration:
# loopback all all all trust       # built-in CockroachDB default
# host  all root all cert-password # CockroachDB mandatory rule
# host  all all  all cert-password map=testing
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
loopback all      all  all     trust
host     all      root all     cert-password
host     all      all  all     cert-password map=testing
# Active identity mapping on this node:
# Original configuration:
# testing testuser node               # Exact remapping
# Active configuration:
# map-name system-username database-username
testing    ^testuser$      node

connect user=testuser database=mydb
----
ERROR: system identity "testuser" mapped to reserved database role "node" (SQLSTATE 28000)

subtest end


# Clean up

set_identity_map
----
# Active authentication configuration on this node:
# Original configuration:
# loopback all all all trust       # built-in CockroachDB default
# host  all root all cert-password # CockroachDB mandatory rule
# host  all all  all cert-password map=testing
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
loopback all      all  all     trust
host     all      root all     cert-password
host     all      all  all     cert-password map=testing

set_hba
----
# Active authentication configuration on this node:
# Original configuration:
# host  all root all cert-password # CockroachDB mandatory rule
# loopback all all all trust       # built-in CockroachDB default
# host     all all all cert-password # built-in CockroachDB default
# local    all all     password      # built-in CockroachDB default
#
# Interpreted configuration:
# TYPE   DATABASE USER ADDRESS METHOD        OPTIONS
host     all      root all     cert-password
loopback all      all  all     trust
host     all      all  all     cert-password
local    all      all          password
