diff --git a/db/comdb2.c b/db/comdb2.c index 7a9c59ec49..00f90f7918 100644 --- a/db/comdb2.c +++ b/db/comdb2.c @@ -238,11 +238,14 @@ int64_t gbl_num_auth_allowed = 0; int64_t gbl_num_auth_denied = 0; int gbl_allow_old_authn = 0; int gbl_uses_externalauth = 0; +int gbl_vtab_externalauth = 1; +int gbl_vtab_externalauth_strict = 0; int gbl_uses_simpleauth = 0; int gbl_uses_externalauth_connect = 0; int gbl_externalauth_warn = 0; int gbl_consumer_auth_warnonly = 1; int gbl_admin_bypass_externalauth = 0; +int gbl_passwords_with_externalauth = 0; int gbl_identity_cache_max = 500; int gbl_authorization_cache_max = 2000; int gbl_authentication_cache_ageout = 900; diff --git a/db/comdb2.h b/db/comdb2.h index 0efe4599dc..69e529bcac 100644 --- a/db/comdb2.h +++ b/db/comdb2.h @@ -1650,11 +1650,14 @@ extern int gbl_allow_old_authn; extern int gbl_uses_password; extern int gbl_unauth_tag_access; extern int gbl_uses_externalauth; +extern int gbl_vtab_externalauth; +extern int gbl_vtab_externalauth_strict; extern int gbl_uses_simpleauth; extern int gbl_uses_externalauth_connect; extern int gbl_externalauth_warn; extern int gbl_consumer_auth_warnonly; extern int gbl_admin_bypass_externalauth; +extern int gbl_passwords_with_externalauth; extern int gbl_identity_cache_max; extern int gbl_authorization_cache_max; extern int gbl_authentication_cache_ageout; diff --git a/db/db_access.c b/db/db_access.c index 4cc5353f05..83ef9056cc 100644 --- a/db/db_access.c +++ b/db/db_access.c @@ -76,6 +76,20 @@ void get_client_origin(char *out, size_t outlen, struct sqlclntstate *clnt) snprintf(out + off, outlen - off, ":driver_version:%s", clnt->api_driver_version); } +static void report_access_denied(const char *action, const char *table, const char *user, int bdberr, errstat_t *err) +{ + char msg[1024]; + if (bdberr) + snprintf(msg, sizeof(msg), "%s access denied to %s for user %s bdberr=%d", action, table, user, bdberr); + else + snprintf(msg, sizeof(msg), "%s access denied to table %s for user %s", action, table, user); + logmsg(LOGMSG_INFO, "%s\n", msg); + if (err) { + errstat_set_rc(err, SQLITE_ACCESS); + errstat_set_str(err, msg); + } +} + int gbl_fdb_auth_error = 0; /* If user password does not match this function @@ -131,7 +145,8 @@ int check_user_password(struct sqlclntstate *clnt) clnt->allow_make_request = 1; ATOMIC_ADD64(gbl_num_auth_allowed, 1); } - return rc; + if (rc || !gbl_passwords_with_externalauth) + return rc; } if ((!remsql_warned || gbl_fdb_auth_error) && (!gbl_uses_password && !gbl_uses_externalauth) && @@ -280,31 +295,20 @@ int access_control_check_sql_write(struct BtCursor *pCur, clnt->argv0 ? clnt->argv0 : "???", clnt->conninfo.pid, clnt->conninfo.node); } else if (externalComdb2AuthenticateUserWrite(clnt->authdata, table_name, client_info)) { ATOMIC_ADD64(gbl_num_auth_denied, 1); - char msg[1024]; - snprintf(msg, sizeof(msg), "Write access denied to table %s for user %s", - table_name, clnt->externalAuthUser ? clnt->externalAuthUser : ""); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); + report_access_denied("Write", table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "", 0, + &thd->clnt->osql.xerr); return SQLITE_ABORT; } - } else { - /* Check read access if its not user schema. */ + } + if ((gbl_passwords_with_externalauth && !clnt->admin) || !gbl_uses_externalauth) { + /* Check write access via password ACLs. */ /* Check it only if engine is open already. */ if (gbl_uses_password && !clnt->current_user.bypass_auth && (thd->clnt->in_sqlite_init == 0)) { - rc = bdb_check_user_tbl_access( - pCur->db->dbenv->bdb_env, thd->clnt->current_user.name, - pCur->db->tablename, ACCESS_WRITE, &bdberr); + rc = bdb_check_user_tbl_access(pCur->db->dbenv->bdb_env, thd->clnt->current_user.name, pCur->db->tablename, + ACCESS_WRITE, &bdberr); if (rc != 0) { ATOMIC_ADD64(gbl_num_auth_denied, 1); - char msg[1024]; - snprintf(msg, sizeof(msg), - "Write access denied to %s for user %s bdberr=%d", - table_name, thd->clnt->current_user.name, bdberr); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); - + report_access_denied("Write", table_name, thd->clnt->current_user.name, bdberr, &thd->clnt->osql.xerr); return SQLITE_ABORT; } } @@ -361,30 +365,18 @@ int access_control_check_sql_read(struct BtCursor *pCur, struct sql_thread *thd, clnt->argv0 ? clnt->argv0 : "???", clnt->conninfo.pid, clnt->conninfo.node); } else if (externalComdb2AuthenticateUserRead(clnt->authdata, table_name, client_info)) { ATOMIC_ADD64(gbl_num_auth_denied, 1); - char msg[1024]; - snprintf(msg, sizeof(msg), "Read access denied to table %s for user %s", - table_name, clnt->externalAuthUser ? clnt->externalAuthUser : ""); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); + report_access_denied("Read", table_name, clnt->externalAuthUser ? clnt->externalAuthUser : "", 0, + &thd->clnt->osql.xerr); return SQLITE_ABORT; } - } else { - if (gbl_uses_password && !clnt->current_user.bypass_auth && pCur && pCur->db && - thd->clnt->in_sqlite_init == 0) { - rc = bdb_check_user_tbl_access( - pCur->db->dbenv->bdb_env, thd->clnt->current_user.name, - pCur->db->tablename, ACCESS_READ, &bdberr); + } + if ((gbl_passwords_with_externalauth && !clnt->admin) || !gbl_uses_externalauth) { + if (gbl_uses_password && !clnt->current_user.bypass_auth && table_name && thd->clnt->in_sqlite_init == 0) { + rc = bdb_check_user_tbl_access(thedb->bdb_env, thd->clnt->current_user.name, (char *)table_name, + ACCESS_READ, &bdberr); if (rc != 0) { ATOMIC_ADD64(gbl_num_auth_denied, 1); - char msg[1024]; - snprintf(msg, sizeof(msg), - "Read access denied to %s for user %s bdberr=%d", - table_name, thd->clnt->current_user.name, bdberr); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); - + report_access_denied("Read", table_name, thd->clnt->current_user.name, bdberr, &thd->clnt->osql.xerr); return SQLITE_ABORT; } } @@ -451,13 +443,16 @@ int access_control_check_read(struct ireq *iq, tran_type *trans, int *bdberr) int comdb2_check_vtab_access(sqlite3 *db, sqlite3_module *module) { HashElem *current; + int dryrun = 0; - if (!gbl_uses_password) { - return 0; + if (!gbl_uses_password && !(gbl_uses_externalauth && gbl_vtab_externalauth)) { + if (gbl_uses_externalauth && !gbl_vtab_externalauth) + dryrun = 1; + else + return 0; } struct sql_thread *thd = pthread_getspecific(query_info_key); - struct sqlclntstate *clnt = thd->clnt; for (current = sqliteHashFirst(&db->aModule); current; current = sqliteHashNext(current)) { @@ -466,51 +461,34 @@ int comdb2_check_vtab_access(sqlite3 *db, sqlite3_module *module) continue; } - int bdberr; - int rc; - if ((module->access_flag == 0) || (module->access_flag & CDB2_ALLOW_ALL)) { return SQLITE_OK; } - if (gbl_uses_externalauth && (thd->clnt->in_sqlite_init == 0) && externalComdb2AuthenticateUserRead && - !(clnt->admin && gbl_admin_bypass_externalauth) && !clnt->current_user.bypass_auth /* not analyze */) { - clnt->authdata = get_authdata(clnt); - char client_info[1024]; - get_client_origin(client_info, sizeof(client_info), clnt); - if (!clnt->authdata && clnt->secure && !gbl_allow_anon_id_for_spmux) - return reject_anon_id(clnt); - if (gbl_externalauth_warn && !clnt->authdata) { - logmsg(LOGMSG_INFO, "Client %s pid:%d mach:%d is missing authentication data\n", - clnt->argv0 ? clnt->argv0 : "???", clnt->conninfo.pid, clnt->conninfo.node); - } else if (externalComdb2AuthenticateUserRead(clnt->authdata, mod->zName, client_info)) { - ATOMIC_ADD64(gbl_num_auth_denied, 1); - char msg[1024]; - snprintf(msg, sizeof(msg), "Read access denied to table %s for user %s", - mod->zName, clnt->externalAuthUser ? clnt->externalAuthUser : ""); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); - return SQLITE_ABORT; - } + if (gbl_uses_externalauth && gbl_vtab_externalauth && !gbl_vtab_externalauth_strict && + !(module->access_flag & CDB2_STRICT)) { return SQLITE_OK; - } else { - rc = bdb_check_user_tbl_access( - thedb->bdb_env, thd->clnt->current_user.name, - (char *)mod->zName, ACCESS_READ, &bdberr); - if (rc != 0) { - char msg[1024]; - snprintf(msg, sizeof(msg), - "Read access denied to %s for user %s bdberr=%d", - mod->zName, thd->clnt->current_user.name, bdberr); - logmsg(LOGMSG_INFO, "%s\n", msg); - errstat_set_rc(&thd->clnt->osql.xerr, SQLITE_ACCESS); - errstat_set_str(&thd->clnt->osql.xerr, msg); - return SQLITE_AUTH; + } + + int rc = access_control_check_sql_read(NULL, thd, (char *)mod->zName); + if (rc != SQLITE_OK) { + if (dryrun) { + struct sqlclntstate *clnt = thd->clnt; + if (errstat_get_rc(&clnt->osql.xerr) == SQLITE_ACCESS) + bzero(&clnt->osql.xerr, sizeof(clnt->osql.xerr)); + static int vtab_access_warned = 0; + if (!vtab_access_warned) { + vtab_access_warned = 1; + logmsg(LOGMSG_WARN, + "vtab access would be denied for %s user %s, enable vtab_externalauth to enforce\n", + mod->zName, clnt->externalAuthUser ? clnt->externalAuthUser : "unknown"); + } + return SQLITE_OK; } - return SQLITE_OK; + return SQLITE_AUTH; } + return SQLITE_OK; } assert(0); return 0; diff --git a/db/db_tunables.h b/db/db_tunables.h index eebaf615ae..8a73a128b8 100644 --- a/db/db_tunables.h +++ b/db/db_tunables.h @@ -2426,6 +2426,12 @@ REGISTER_TUNABLE("externalauth", NULL, TUNABLE_BOOLEAN, &gbl_uses_externalauth, REGISTER_TUNABLE("externalauth_connect", "Check for externalauth only once on connect", TUNABLE_BOOLEAN, &gbl_uses_externalauth_connect, NOARG | READEARLY, NULL, NULL, NULL, NULL); +REGISTER_TUNABLE("vtab_externalauth", "Use IAM for vtab access control (Default: on)", TUNABLE_BOOLEAN, + &gbl_vtab_externalauth, 0, NULL, NULL, NULL, NULL); + +REGISTER_TUNABLE("vtab_externalauth_strict", "Enforce access control on all CDB2_ALLOW_USER vtabs (Default: off)", + TUNABLE_BOOLEAN, &gbl_vtab_externalauth_strict, 0, NULL, NULL, NULL, NULL); + REGISTER_TUNABLE("externalauth_warn", "Warn instead of returning error in case of missing authdata", TUNABLE_BOOLEAN, &gbl_externalauth_warn, NOARG | READEARLY, NULL, NULL, NULL, NULL); @@ -2437,6 +2443,9 @@ REGISTER_TUNABLE("consumer_auth_warnonly", REGISTER_TUNABLE("admin_bypass_externalauth", "Allow admin connections to bypass external auth checks (Default: OFF)", TUNABLE_BOOLEAN, &gbl_admin_bypass_externalauth, NOARG | READEARLY, NULL, NULL, NULL, NULL); +REGISTER_TUNABLE("passwords_with_externalauth", "Check password auth in addition to externalauth (Default: off)", + TUNABLE_BOOLEAN, &gbl_passwords_with_externalauth, NOARG | READEARLY, NULL, NULL, NULL, NULL); + REGISTER_TUNABLE("view_feature", "Enables support for VIEWs (Default: ON)", TUNABLE_BOOLEAN, &gbl_view_feature, 0, NULL, NULL, NULL, NULL); diff --git a/lua/sp.c b/lua/sp.c index 455060cb3a..6e40c1b9f1 100644 --- a/lua/sp.c +++ b/lua/sp.c @@ -7256,7 +7256,7 @@ static int exec_procedure_int(struct sqlthdstate *thd, // Use () to differentiate between tablename and spname snprintf(spfunc, sizeof(spfunc), "%s()", spname); - if (access_control_check_sql_read(NULL, sqlthd, spfunc)) { + if (gbl_uses_externalauth && access_control_check_sql_read(NULL, sqlthd, spfunc)) { if (consumer && gbl_consumer_auth_warnonly) { static int logged = 0; if (!logged) { diff --git a/sqlite/ext/comdb2/activeosqls.c b/sqlite/ext/comdb2/activeosqls.c index ade3576c90..fbe3982f27 100644 --- a/sqlite/ext/comdb2/activeosqls.c +++ b/sqlite/ext/comdb2/activeosqls.c @@ -168,7 +168,7 @@ static void free_osqls(void *p, int n) } sqlite3_module systblActiveOsqlsModule = { - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; int systblActiveOsqlsInit(sqlite3 *db) diff --git a/sqlite/ext/comdb2/connections.c b/sqlite/ext/comdb2/connections.c index 7e0e537f11..60fd1bc2c8 100644 --- a/sqlite/ext/comdb2/connections.c +++ b/sqlite/ext/comdb2/connections.c @@ -26,7 +26,7 @@ #include "types.h" sqlite3_module systblConnectionsModule = { - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; int get_connections(void **data, int *num_points) { diff --git a/sqlite/ext/comdb2/files.c b/sqlite/ext/comdb2/files.c index a3056d3eec..4124a2295f 100644 --- a/sqlite/ext/comdb2/files.c +++ b/sqlite/ext/comdb2/files.c @@ -270,4 +270,4 @@ const sqlite3_module systblFilesModule = { 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ - .access_flag = CDB2_ALLOW_USER}; + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT}; diff --git a/sqlite/ext/comdb2/logicalops.c b/sqlite/ext/comdb2/logicalops.c index cb6f40337e..e1efa5283f 100644 --- a/sqlite/ext/comdb2/logicalops.c +++ b/sqlite/ext/comdb2/logicalops.c @@ -1262,7 +1262,7 @@ sqlite3_module systblLogicalOpsModule = { 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; diff --git a/sqlite/ext/comdb2/sample_queries.c b/sqlite/ext/comdb2/sample_queries.c index fadead587a..5867d4176f 100644 --- a/sqlite/ext/comdb2/sample_queries.c +++ b/sqlite/ext/comdb2/sample_queries.c @@ -28,7 +28,7 @@ extern hash_t *gbl_sample_queries_hash; extern pthread_mutex_t gbl_fingerprint_hash_mu; sqlite3_module systblSampleQueriesModule = { - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; typedef struct systable_sample_queries { diff --git a/sqlite/ext/comdb2/sqlpoolqueue.c b/sqlite/ext/comdb2/sqlpoolqueue.c index 1bafb95831..e9b2c2f99b 100644 --- a/sqlite/ext/comdb2/sqlpoolqueue.c +++ b/sqlite/ext/comdb2/sqlpoolqueue.c @@ -80,7 +80,7 @@ static void free_sqlpoolqueue(void *p, int n) } sqlite3_module systblSqlpoolQueueModule = { - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; int systblSqlpoolQueueInit(sqlite3 *db) { diff --git a/sqlite/ext/comdb2/stringrefs.c b/sqlite/ext/comdb2/stringrefs.c index 6a595a8103..4c9f2e7df5 100644 --- a/sqlite/ext/comdb2/stringrefs.c +++ b/sqlite/ext/comdb2/stringrefs.c @@ -84,7 +84,7 @@ static void free_stringrefs(void *p, int n) } sqlite3_module systblStringRefsModule = { - .access_flag = CDB2_ALLOW_USER, + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT, }; int systblStringRefsInit(sqlite3 *db) { diff --git a/sqlite/ext/comdb2/tranlog.c b/sqlite/ext/comdb2/tranlog.c index 9969007491..f0dc434058 100644 --- a/sqlite/ext/comdb2/tranlog.c +++ b/sqlite/ext/comdb2/tranlog.c @@ -756,7 +756,7 @@ sqlite3_module systblTransactionLogsModule = { 0, /* xRelease */ 0, /* xRollbackTo */ 0, /* xShadowName */ - .access_flag = CDB2_ALLOW_USER + .access_flag = CDB2_ALLOW_USER | CDB2_STRICT }; diff --git a/sqlite/src/sqlite.h.in b/sqlite/src/sqlite.h.in index ecacf3e40b..f8d9329b26 100644 --- a/sqlite/src/sqlite.h.in +++ b/sqlite/src/sqlite.h.in @@ -6523,6 +6523,7 @@ enum { CDB2_ALLOW_USER = 1<<1, /* Limit access only to permitted users */ CDB2_HIDDEN = 1<<2, /* Remove it from system table list (comdb2_systables) */ + CDB2_STRICT = 1<<3, /* Always enforce access check */ }; #endif /* defined(SQLITE_BUILDING_FOR_COMDB2) */ diff --git a/sqlite/src/sqlite3.h b/sqlite/src/sqlite3.h index a7302053a6..e01e5bfa75 100644 --- a/sqlite/src/sqlite3.h +++ b/sqlite/src/sqlite3.h @@ -6541,6 +6541,7 @@ enum { CDB2_ALLOW_USER = 1<<1, /* Limit access only to permitted users */ CDB2_HIDDEN = 1<<2, /* Remove it from system table list (comdb2_systables) */ + CDB2_STRICT = 1<<3, /* Always enforce access check */ }; #endif /* defined(SQLITE_BUILDING_FOR_COMDB2) */ diff --git a/tests/simpleauth.test/Makefile b/tests/simpleauth.test/Makefile index b4c0ac1057..e05866ba3a 100644 --- a/tests/simpleauth.test/Makefile +++ b/tests/simpleauth.test/Makefile @@ -4,5 +4,5 @@ else include $(TESTSROOTDIR)/testcase.mk endif ifeq ($(TEST_TIMEOUT),) - export TEST_TIMEOUT=5m + export TEST_TIMEOUT=1m endif diff --git a/tests/simpleauth.test/lrl.options b/tests/simpleauth.test/lrl.options index 6b1bc80f5b..a9b2ec02d8 100644 --- a/tests/simpleauth.test/lrl.options +++ b/tests/simpleauth.test/lrl.options @@ -1 +1,2 @@ simpleauth +vtab_externalauth diff --git a/tests/simpleauth.test/runit b/tests/simpleauth.test/runit index 1d1796623e..8f2d749c0d 100755 --- a/tests/simpleauth.test/runit +++ b/tests/simpleauth.test/runit @@ -196,6 +196,92 @@ run_expect_success "$OP" "drop procedure myproc version 'test'" \ run_expect_success "$OP" "drop table if exists t_udf" \ "drop t_udf table" +#--------------------------------------------------------------- +echo "Test: vtab access control with externalauth (CDB2_ALLOW_ALL)" +#--------------------------------------------------------------- +# CDB2_ALLOW_ALL vtabs should be accessible to any authenticated user +# regardless of specific permissions, since comdb2_check_vtab_access +# returns SQLITE_OK immediately for these modules. +run_expect_success "$MOHIT" "select 1 from comdb2_tables limit 1" \ + "mohit can access comdb2_tables (CDB2_ALLOW_ALL)" +run_expect_success "$MIKE" "select 1 from comdb2_columns limit 1" \ + "mike can access comdb2_columns (CDB2_ALLOW_ALL)" +run_expect_success "$MARK" "select 1 from comdb2_keywords limit 1" \ + "mk can access comdb2_keywords (CDB2_ALLOW_ALL)" +run_expect_success "$MOHIT" "select 1 from comdb2_cluster limit 1" \ + "mohit can access comdb2_cluster (CDB2_ALLOW_ALL)" + +#--------------------------------------------------------------- +echo "Test: vtab access control with externalauth (CDB2_ALLOW_USER, wildcard)" +#--------------------------------------------------------------- +# CDB2_ALLOW_USER vtabs should be accessible when a wildcard +# permission rule is in place. +run_expect_success "$MOHIT" "select 1 from comdb2_tunables limit 1" \ + "mohit can access comdb2_tunables (wildcard allows)" +run_expect_success "$MIKE" "select 1 from comdb2_metrics limit 1" \ + "mike can access comdb2_metrics (wildcard allows)" +run_expect_success "$MARK" "select 1 from comdb2_connections limit 1" \ + "mk can access comdb2_connections (wildcard allows)" +run_expect_success "$MOHIT" "select 1 from comdb2_files limit 1" \ + "mohit can access comdb2_files (wildcard allows)" + +#--------------------------------------------------------------- +echo "Test: vtab access denied without matching permission" +#--------------------------------------------------------------- +# Remove wildcard rule and verify CDB2_ALLOW_USER vtabs are denied +# for users without specific permission. +run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'op', '*', 'Write', 'table', 'comdb2_simple_auth') on conflict do nothing" \ + "grant op write on auth table (vtab test)" +run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'op', '*', 'Read', 'table', 'comdb2_simple_auth') on conflict do nothing" \ + "grant op read on auth table (vtab test)" +run_expect_success "$OP" "delete from comdb2_simple_auth where cluster='*' and user='*' and bpkg='*' and verb='*' and resourcetype='*' and resourcename='*'" \ + "remove wildcard rule (vtab test)" + +# CDB2_ALLOW_USER vtabs (non-strict) are allowed in non-strict mode +run_expect_success "$MOHIT" "select 1 from comdb2_tunables limit 1" \ + "mohit can access comdb2_tunables (non-strict, no matching rule)" +run_expect_success "$MIKE" "select 1 from comdb2_metrics limit 1" \ + "mike can access comdb2_metrics (non-strict, no matching rule)" + +# CDB2_STRICT vtabs should be denied without matching permission +run_expect_failure "$MARK" "select 1 from comdb2_connections limit 1" \ + "mk cannot access comdb2_connections (CDB2_STRICT, no matching rule)" +run_expect_failure "$MOHIT" "select 1 from comdb2_files limit 1" \ + "mohit cannot access comdb2_files (CDB2_STRICT, no matching rule)" + +# CDB2_ALLOW_ALL vtabs should still be accessible without any permission +run_expect_success "$MOHIT" "select 1 from comdb2_tables limit 1" \ + "mohit can still access comdb2_tables (CDB2_ALLOW_ALL, no wildcard)" +run_expect_success "$MIKE" "select 1 from comdb2_keywords limit 1" \ + "mike can still access comdb2_keywords (CDB2_ALLOW_ALL, no wildcard)" + +#--------------------------------------------------------------- +echo "Test: vtab access granted with specific permission" +#--------------------------------------------------------------- +# Grant mohit specific Read access to comdb2_connections (CDB2_STRICT) +run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'mohit', '*', 'Read', 'table', 'comdb2_connections') on conflict do nothing" \ + "grant mohit Read on comdb2_connections" +run_expect_success "$MOHIT" "select 1 from comdb2_connections limit 1" \ + "mohit can access comdb2_connections (specific rule)" +# mike still denied (no rule for him, CDB2_STRICT) +run_expect_failure "$MIKE" "select 1 from comdb2_connections limit 1" \ + "mike still cannot access comdb2_connections (CDB2_STRICT, no rule)" + +# Grant mike specific Read access to comdb2_files (CDB2_STRICT) +run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', 'mike', '*', 'Read', 'table', 'comdb2_files') on conflict do nothing" \ + "grant mike Read on comdb2_files" +run_expect_success "$MIKE" "select 1 from comdb2_files limit 1" \ + "mike can access comdb2_files (specific rule)" +# mohit still denied (no rule for her, CDB2_STRICT) +run_expect_failure "$MOHIT" "select 1 from comdb2_files limit 1" \ + "mohit still cannot access comdb2_files (CDB2_STRICT, no rule)" + +# Restore wildcard and clean up vtab-specific rules +run_expect_success "$OP" "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing" \ + "restore wildcard rule (vtab test)" +run_expect_success "$OP" "delete from comdb2_simple_auth where user != '*'" \ + "clean up user-specific rules (vtab test)" + #--------------------------------------------------------------- echo "Test: anonymous access" #--------------------------------------------------------------- diff --git a/tests/simpleauth_password.test/Makefile b/tests/simpleauth_password.test/Makefile new file mode 100644 index 0000000000..2d39e80074 --- /dev/null +++ b/tests/simpleauth_password.test/Makefile @@ -0,0 +1,8 @@ +ifeq ($(TESTSROOTDIR),) + include ../testcase.mk +else + include $(TESTSROOTDIR)/testcase.mk +endif +ifeq ($(TEST_TIMEOUT),) + export TEST_TIMEOUT=3m +endif diff --git a/tests/simpleauth_password.test/dummy.csc2 b/tests/simpleauth_password.test/dummy.csc2 new file mode 100644 index 0000000000..8c214679f1 --- /dev/null +++ b/tests/simpleauth_password.test/dummy.csc2 @@ -0,0 +1,4 @@ +schema +{ + byte dummy[1] +} diff --git a/tests/simpleauth_password.test/lrl.options b/tests/simpleauth_password.test/lrl.options new file mode 100644 index 0000000000..6b396d7e13 --- /dev/null +++ b/tests/simpleauth_password.test/lrl.options @@ -0,0 +1,5 @@ +simpleauth +passwords_with_externalauth +admin_bypass_externalauth +table dummy dummy.csc2 +create_dba_user off diff --git a/tests/simpleauth_password.test/runit b/tests/simpleauth_password.test/runit new file mode 100755 index 0000000000..39c2979c21 --- /dev/null +++ b/tests/simpleauth_password.test/runit @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +bash -n "$0" | exit 1 + +set -e +source ${TESTSROOTDIR}/tools/runit_common.sh + +dbnm=$1 + +if [ "x$dbnm" == "x" ] ; then + failexit "need a DB name" +fi + +SA="${TESTSBUILDDIR}/simpleauth_test ${dbnm}" +OP="bpi:procauth:cluster:test:user:op:bpkg:setup" +MOHIT="bpi:procauth:cluster:testcluster:user:mohit:bpkg:myapp" + +passed=0 +failed=0 + +run_expect_success() { + local principal="$1" + local sql="$2" + local desc="$3" + if $SA "$principal" "$sql" > /dev/null 2>&1; then + echo " PASS: $desc" + passed=$((passed + 1)) + else + echo " FAIL: $desc" + failed=$((failed + 1)) + fi +} + +run_expect_failure() { + local principal="$1" + local sql="$2" + local desc="$3" + if $SA "$principal" "$sql" > /dev/null 2>&1; then + echo " FAIL: $desc (expected failure but succeeded)" + failed=$((failed + 1)) + else + echo " PASS: $desc" + passed=$((passed + 1)) + fi +} + +# Run SQL via admin connection with OP password user (bypasses IAM). +admin_sql() { + local sql="$1" + ${CDB2SQL_EXE} --admin ${CDB2_OPTIONS} ${dbnm} default - < IAM table check fails +# before reaching the password ACL check. +run_expect_failure "$MOHIT" "select * from t1" \ + "read t1: IAM denied + password pass (fails at IAM)" +run_expect_failure "$MOHIT" "insert into t1 values(3)" \ + "write t1: IAM denied + password pass (fails at IAM)" +# Restore wildcard IAM rule +admin_sql "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing" +# Verify restored +run_expect_success "$MOHIT" "select * from t1" \ + "read t1 after IAM restore: both pass" + +#--------------------------------------------------------------- +echo "Test: Neither IAM nor password passes" +#--------------------------------------------------------------- +admin_sql "delete from comdb2_simple_auth where cluster='*' and user='*' and bpkg='*' and verb='*' and resourcetype='*' and resourcename='*'" +admin_sql "revoke read on t1 from 'default'" +admin_sql "revoke write on t1 from 'default'" +run_expect_failure "$MOHIT" "select * from t1" \ + "read t1: IAM denied + password denied" +run_expect_failure "$MOHIT" "insert into t1 values(4)" \ + "write t1: IAM denied + password denied" + +#--------------------------------------------------------------- +echo "Test: Cleanup" +#--------------------------------------------------------------- +admin_sql "insert into comdb2_simple_auth(cluster, user, bpkg, verb, resourcetype, resourcename) values('*', '*', '*', '*', '*', '*') on conflict do nothing" +admin_sql "drop table if exists t1" +admin_sql "delete from comdb2_simple_auth where user != '*'" + +echo "" +echo "Results: $passed passed, $failed failed" + +if [ $failed -gt 0 ] ; then + failexit "$failed tests failed" +fi + +echo "Success" +exit 0 diff --git a/tests/tunables.test/t00_all_tunables.expected b/tests/tunables.test/t00_all_tunables.expected index d3fe3a9fc7..5bb3ad3c68 100644 --- a/tests/tunables.test/t00_all_tunables.expected +++ b/tests/tunables.test/t00_all_tunables.expected @@ -718,6 +718,7 @@ (name='partition_retroactively_verbose', description='Disable/enable data routing debugging for retroactively time partitioning (Default: OFF)', type='BOOLEAN', value='OFF', read_only='N') (name='partition_sc_reorder', description='If the schema change is serialized for a partition, run current shard last', type='BOOLEAN', value='ON', read_only='N') (name='partitioned_table_enabled', description='Allow syntax create/alter table ... partitioned by ...', type='BOOLEAN', value='ON', read_only='N') +(name='passwords_with_externalauth', description='Check password auth in addition to externalauth (Default: off)', type='BOOLEAN', value='OFF', read_only='N') (name='pause_moveto', description='pause_moveto', type='BOOLEAN', value='OFF', read_only='N') (name='pbkdf2_iterations', description='Number of iterations of PBKDF2 algorithm for password hashing.', type='INTEGER', value='4096', read_only='N') (name='penaltyincpercent', description='', type='INTEGER', value='20', read_only='Y') @@ -1147,6 +1148,8 @@ (name='view_feature', description='Enables support for VIEWs (Default: ON)', type='BOOLEAN', value='ON', read_only='N') (name='views_dft_preempt_roll_secs', description='Amount of seconds to run phase 1 of time partition rollout before phase 2', type='INTEGER', value='1800', read_only='N') (name='views_dft_roll_delete_lag_secs', description='Amount of seconds to run phase 3 of time partition rollout after phase 2', type='INTEGER', value='5', read_only='N') +(name='vtab_externalauth', description='Use IAM for vtab access control (Default: on)', type='BOOLEAN', value='ON', read_only='N') +(name='vtab_externalauth_strict', description='Enforce access control on all CDB2_ALLOW_USER vtabs (Default: off)', type='BOOLEAN', value='OFF', read_only='N') (name='wait_for_prepare_seqnum', description='Wait-for-seqnum for prepare records. (Default: on)', type='BOOLEAN', value='ON', read_only='N') (name='wait_for_seqnum_trace', description='', type='BOOLEAN', value='OFF', read_only='N') (name='wal_osync', description='Open WAL files using the O_SYNC flag (Default: off)', type='BOOLEAN', value='OFF', read_only='N')