2021-04-26 20:07:00 +02:00
|
|
|
#coding:utf-8
|
|
|
|
|
2022-02-04 19:05:19 +01:00
|
|
|
"""
|
|
|
|
ID: syspriv.change-shutdown-mode
|
|
|
|
TITLE: Check ability to change database shutdown mode by non-sysdba user who is
|
|
|
|
granted with necessary system privileges
|
|
|
|
DESCRIPTION:
|
2022-05-20 09:40:08 +02:00
|
|
|
Test creates common user and role, then it grants system privileged to that role
|
|
|
|
and, in turn, grants role to user. Because role is granted as DEFAULT, we can connect
|
|
|
|
to services without specifying it.
|
|
|
|
Further, we change DB state to shutdown and bring it online several times, using this
|
|
|
|
non-DBA user account. All these actions must not raise exception.
|
|
|
|
NB: role must be granted with 'IGNORE_DB_TRIGGERS' privilege in order to bypass DB-level
|
|
|
|
triggers when user is attaching to DB which has such triggers. Test verifies this by adding
|
|
|
|
table which is filled by DB-level trigger on connect. This table must remain EMPTY at the
|
|
|
|
final point of the test, see 'SELECT COUNT(*) FROM ATT_LOG' query.
|
|
|
|
|
2022-02-04 19:05:19 +01:00
|
|
|
FBTEST: functional.syspriv.change_shutdown_mode
|
2022-05-20 09:40:08 +02:00
|
|
|
NOTES:
|
|
|
|
[20.05.2022] pzotov
|
2022-05-20 18:31:21 +02:00
|
|
|
In order to verify ability to change DB state to SINGLE or MULTI, one need to grant system privilege
|
|
|
|
ACCESS_SHUTDOWN_DATABASE, oterwise attempt to change DB state to such mode will fail with error:
|
|
|
|
"database ... shutdown".
|
|
|
|
Explanation: https://github.com/FirebirdSQL/firebird/issues/7189#issuecomment-1132731509
|
2022-05-20 09:40:08 +02:00
|
|
|
|
|
|
|
Checked on 4.0.1.2692, 5.0.0.497.
|
2022-02-04 19:05:19 +01:00
|
|
|
"""
|
2021-04-26 20:07:00 +02:00
|
|
|
|
2022-02-04 19:05:19 +01:00
|
|
|
import pytest
|
|
|
|
from firebird.qa import *
|
2022-05-20 09:40:08 +02:00
|
|
|
from firebird.driver import ShutdownMode,ShutdownMethod
|
|
|
|
from firebird.driver.types import DatabaseError
|
|
|
|
|
|
|
|
substitutions = [('[ \t]+', ' ')]
|
|
|
|
db = db_factory()
|
|
|
|
tmp_user = user_factory('db', name='tmp_syspriv_user', password='123')
|
|
|
|
tmp_role = role_factory('db', name='tmp_role_for_chng_shutdown_mode')
|
|
|
|
|
|
|
|
act = python_act('db', substitutions = substitutions)
|
|
|
|
|
|
|
|
expected_stdout_isql = "ATT_LOG_COUNT 0"
|
2021-04-26 20:07:00 +02:00
|
|
|
|
2022-02-04 19:05:19 +01:00
|
|
|
|
|
|
|
@pytest.mark.version('>=4.0')
|
2022-05-20 09:40:08 +02:00
|
|
|
def test_1(act: Action, tmp_user: User, tmp_role:Role, capsys):
|
|
|
|
|
|
|
|
#-----------------------------------------------
|
2022-05-20 18:31:21 +02:00
|
|
|
def bring_tmp_db_online( srv, db_path ):
|
2022-05-20 09:40:08 +02:00
|
|
|
srv.database.bring_online(database=db_path)
|
|
|
|
#-----------------------------------------------
|
|
|
|
|
|
|
|
init_script = \
|
|
|
|
f'''
|
|
|
|
set wng off;
|
|
|
|
set bail on;
|
|
|
|
set list on;
|
|
|
|
set count on;
|
|
|
|
|
|
|
|
create or alter view v_check as
|
|
|
|
select
|
|
|
|
current_user as who_ami
|
|
|
|
,r.rdb$role_name
|
|
|
|
,rdb$role_in_use(r.rdb$role_name) as RDB_ROLE_IN_USE
|
|
|
|
,r.rdb$system_privileges
|
|
|
|
from mon$database m cross join rdb$roles r;
|
|
|
|
commit;
|
|
|
|
|
|
|
|
alter user {tmp_user.name} password '123' revoke admin role;
|
|
|
|
revoke all on all from {tmp_user.name};
|
|
|
|
|
|
|
|
create or alter trigger trg_connect active on connect as
|
|
|
|
begin
|
|
|
|
end;
|
|
|
|
commit;
|
|
|
|
|
|
|
|
recreate table att_log (
|
|
|
|
att_id int,
|
|
|
|
att_name varchar(255),
|
|
|
|
att_user varchar(255),
|
|
|
|
att_addr varchar(255),
|
|
|
|
att_prot varchar(255),
|
|
|
|
att_dts timestamp default 'now'
|
|
|
|
);
|
|
|
|
|
|
|
|
commit;
|
|
|
|
|
|
|
|
grant select on v_check to public;
|
|
|
|
grant all on att_log to public;
|
|
|
|
commit;
|
|
|
|
|
|
|
|
set term ^;
|
|
|
|
create or alter trigger trg_connect active on connect as
|
|
|
|
begin
|
|
|
|
if ( upper(current_user) <> upper('SYSDBA') ) then
|
|
|
|
in autonomous transaction do
|
|
|
|
insert into att_log(att_id, att_name, att_user, att_prot)
|
|
|
|
select
|
|
|
|
mon$attachment_id
|
|
|
|
,mon$attachment_name
|
|
|
|
,mon$user
|
|
|
|
,mon$remote_protocol
|
|
|
|
from mon$attachments
|
|
|
|
where mon$user = current_user
|
|
|
|
;
|
|
|
|
end
|
|
|
|
^
|
|
|
|
set term ;^
|
|
|
|
commit;
|
|
|
|
|
|
|
|
|
|
|
|
-- NB: Privilege 'IGNORE_DB_TRIGGERS' is needed when we return database to ONLINE
|
|
|
|
-- and this DB has DB-level trigger.
|
|
|
|
alter role {tmp_role.name}
|
2022-05-20 18:31:21 +02:00
|
|
|
set system privileges to CHANGE_SHUTDOWN_MODE, USE_GFIX_UTILITY, IGNORE_DB_TRIGGERS, ACCESS_SHUTDOWN_DATABASE;
|
2022-05-20 09:40:08 +02:00
|
|
|
commit;
|
|
|
|
grant default {tmp_role.name} to user {tmp_user.name};
|
|
|
|
commit;
|
|
|
|
'''
|
|
|
|
act.isql(switches=['-q'], input=init_script)
|
|
|
|
|
|
|
|
with act.connect_server(user = tmp_user.name, password = tmp_user.password, role = tmp_role.name) as srv_nondba:
|
|
|
|
# All subsequent actions must not issue any output:
|
|
|
|
try:
|
2022-05-20 18:31:21 +02:00
|
|
|
for checked_shut_mode in (ShutdownMode.FULL, ShutdownMode.MULTI, ShutdownMode.SINGLE):
|
|
|
|
for checked_shut_method in (ShutdownMethod.FORCED, ShutdownMethod.DENNY_ATTACHMENTS, ShutdownMethod.DENNY_TRANSACTIONS):
|
|
|
|
for checked_timeout in (0,1):
|
|
|
|
srv_nondba.database.shutdown(database=act.db.db_path
|
|
|
|
,mode=checked_shut_mode
|
|
|
|
,method=checked_shut_method
|
|
|
|
,timeout=checked_timeout)
|
|
|
|
bring_tmp_db_online(srv_nondba, act.db.db_path)
|
2022-05-20 09:40:08 +02:00
|
|
|
|
|
|
|
except DatabaseError as e:
|
|
|
|
print(e.__str__())
|
|
|
|
|
|
|
|
|
|
|
|
sql_check = "set list on; select count(*) as att_log_count from att_log;"
|
|
|
|
act.isql(switches=['-q'], input=sql_check)
|
|
|
|
act.expected_stdout = expected_stdout_isql
|
|
|
|
assert act.clean_stdout == act.clean_expected_stdout
|
|
|
|
|