diff --git a/spec/acceptance/sqlserver_database_spec.rb b/spec/acceptance/sqlserver_database_spec.rb index d1b4d9bf..ae23df6d 100644 --- a/spec/acceptance/sqlserver_database_spec.rb +++ b/spec/acceptance/sqlserver_database_spec.rb @@ -29,9 +29,7 @@ def run_sql_query_opts (query, expected_row_count) end after(:each) do - # delete created database: - pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': admin_user => 'sa', @@ -41,8 +39,7 @@ def run_sql_query_opts (query, expected_row_count) ensure => 'absent', } MANIFEST - #comment out the below line because of ticket MODULES-2554. - #ensure_sqlserver_database(host, pp) + ensure_sqlserver_database(host, pp) end it "Test Case C89019: Create a database" do @@ -67,6 +64,37 @@ def run_sql_query_opts (query, expected_row_count) run_sql_query(host, run_sql_query_opts(query, 1)) end + it "Delete a database" do + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::database{'#{@db_name}': + } + MANIFEST + ensure_sqlserver_database(host, pp) + + puts "Validate the Database '#{@db_name}' exists" + query = "SELECT database_id from sys.databases WHERE name = '#{@db_name}';" + run_sql_query(host, run_sql_query_opts(query, 1)) + + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::database{'#{@db_name}': + ensure => absent, + } + MANIFEST + ensure_sqlserver_database(host, pp) + + puts "Validate the Database '#{@db_name}' does not exist" + query = "SELECT database_id from sys.databases WHERE name = '#{@db_name}';" + run_sql_query(host, run_sql_query_opts(query, 0)) + end + it "Test Case C89076: Create database with optional collation_name" do pp = <<-MANIFEST sqlserver::config{'MSSQLSERVER': diff --git a/spec/acceptance/sqlserver_login_spec.rb b/spec/acceptance/sqlserver_login_spec.rb index d08555ed..8e7787f8 100644 --- a/spec/acceptance/sqlserver_login_spec.rb +++ b/spec/acceptance/sqlserver_login_spec.rb @@ -5,6 +5,7 @@ db_name = ("DB" + SecureRandom.hex(4)).upcase table_name = 'Tables_' + SecureRandom.hex(3) +# Covers testrail => ['89118', '89119', '89120', '89121', '89122', '89123', '89124', '89125', '89540'] describe "Test sqlserver::login", :node => host do def ensure_manifest_apply(host, pp) @@ -22,212 +23,328 @@ def run_sql_query_opts (user, passwd, query, expected_row_count) :expected_row_count => expected_row_count, } end + def run_sql_query_opts_as_sa (query, expected_row_count) + run_sql_query_opts('sa','Pupp3t1@',query,expected_row_count) + end - context "Start testing...", {:testrail => ['89118', '89119', '89120', '89121', '89122', '89123', '89124', '89125', '89540']} do - - before(:all) do - # Create a database and a simple table to use for all the tests - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::database{'#{db_name}': - } - sqlserver_tsql{'testsqlserver_tsql': - instance => 'MSSQLSERVER', - database => '#{db_name}', - command => "CREATE TABLE #{table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", - require => Sqlserver::Database['#{db_name}'], - } - MANIFEST - ensure_manifest_apply(host, pp) - end + def remove_test_logins(host) + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::login{'#{@login_user}': + instance => 'MSSQLSERVER', + ensure => 'absent', + } + sqlserver::login{'#{@login_windows_user}': + instance => 'MSSQLSERVER', + ensure => 'absent', + } + sqlserver::login{'#{@login_windows_group}': + instance => 'MSSQLSERVER', + ensure => 'absent', + } + MANIFEST + ensure_manifest_apply(host, pp) + end - # Delete Database after all tests are done - after(:all) do - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::database{'#{db_name}': - instance => 'MSSQLSERVER', - ensure => 'absent', - } - MANIFEST - #comment out the below line because of ticket MODULES-2554(delete database) - #ensure_manifest_apply(host, pp) + def create_login_manifest (testcase,login_name,login_password,options = {}) + case testcase + when 'SQL_LOGIN user' + login_type = 'SQL_LOGIN' + when 'WINDOWS_LOGIN user' + login_type = 'WINDOWS_LOGIN' + when 'WINDOWS_LOGIN group' + login_type = 'WINDOWS_LOGIN' + else + raise "Unknown testcase name #{testcase}" end - # Generate different set of sqlserver login/password for each test - before(:each) do - @login_user = "Login" + SecureRandom.hex(4) - @login_passwd = "Password" + SecureRandom.hex(5) - end + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::login{'#{login_name}': + instance => 'MSSQLSERVER', + login_type => '#{login_type}', + #{ if testcase == 'SQL_LOGIN user' then "password => '#{login_password}'," end } + #{ options['svrroles'].nil? ? "svrroles => {'sysadmin' => 1}," : "svrroles => #{options['svrroles']}," } + #{ "check_expiration => #{options['check_expiration']}," unless options['check_expiration'].nil? } + #{ "check_policy => #{options['check_policy']}," unless options['check_policy'].nil? } + #{ "default_database => '#{options['default_database']}'," unless options['default_database'].nil? } + #{ "default_language => '#{options['default_language']}'," unless options['default_language'].nil? } + #{ "disabled => #{options['disabled']}," unless options['disabled'].nil? } + #{ "ensure => '#{options['ensure']}'," unless options['ensure'].nil? } + } + MANIFEST + end - after(:each) do - # delete recently created login after each test: - #This test also cover test case C89540: Delete login - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - ensure => 'absent', - } - MANIFEST - #comment out the below line because of ticket MODULES-2323(delete login) - #ensure_manifest_apply(host, pp) - end + before(:all) do + @login_user = "Login" + SecureRandom.hex(4) + @login_passwd = "Password1!" + SecureRandom.hex(5) + @windows_user = "User" + SecureRandom.hex(4) + @windows_group = "Group" + SecureRandom.hex(4) - it "Test Case C89118: create login with optional 'check_expiration'" do - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - login_type => 'SQL_LOGIN', - password => '#{@login_passwd}', - svrroles => {'sysadmin' => 1}, - check_expiration => true, - } - - MANIFEST - ensure_manifest_apply(host, pp) - - puts "Validate the login '#{@login_user}' is successfully created and able to access database '#{db_name}':" - query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - - puts "Validate the login '#{@login_user}' is successfully created and has correct is_expiration_checked:" - query = "SELECT name as LOGIN_NAME, is_expiration_checked - FROM SYS.SQL_LOGINS - WHERE is_expiration_checked = '1' - AND name = '#{@login_user}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - end + host_shortname = on(host,'hostname').stdout.upcase.strip # Require the NETBIOS name for later database searches + @login_windows_user = host_shortname + '\\' + @windows_user + @login_windows_group = host_shortname + '\\' + @windows_group - it "Test Case C89119: create login with optional 'check_policy'" do - #This test also cover test case C89123: create login with optional 'instance' - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - login_type => 'SQL_LOGIN', - password => '#{@login_passwd}', - svrroles => {'sysadmin' => 1}, - check_policy => true, - } - - MANIFEST - ensure_manifest_apply(host, pp) - - puts "Validate the login '#{@login_user}' is successfully created and able to access database '#{db_name}':" - query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - - puts "Validate the login '#{@login_user}' is successfully created and has correct is_expiration_checked:" - query = "SELECT name as LOGIN_NAME, is_policy_checked - FROM SYS.SQL_LOGINS - WHERE is_policy_checked = '1' - AND name = '#{@login_user}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - end + # Create a database, a simple table and windows accounts fixtures + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::database{'#{db_name}': + } + sqlserver_tsql{'testsqlserver_tsql': + instance => 'MSSQLSERVER', + database => '#{db_name}', + command => "CREATE TABLE #{table_name} (id INT, name VARCHAR(20), email VARCHAR(20));", + require => Sqlserver::Database['#{db_name}'], + } - it "Test Case C89120: create login with optional 'default_database'" do - #This test also cover test case C89124: create login with optional 'login_type' - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - login_type => 'SQL_LOGIN', - password => '#{@login_passwd}', - svrroles => {'sysadmin' => 1}, - default_database => '#{db_name}', - } - - MANIFEST - ensure_manifest_apply(host, pp) - - puts "Validate the login '#{@login_user}' is successfully created and able to access database '#{db_name}':" - query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - - puts "Validate the login '#{@login_user}' is successfully created and has correct default_database:" - query = "SELECT name as LOGIN_NAME, default_database_name - FROM SYS.SQL_LOGINS - WHERE default_database_name = '#{db_name}' - AND name = '#{@login_user}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - end + user {'#{@windows_user}': + password => '#{@login_passwd}', + ensure => 'present', + } + group {'#{@windows_group}': + ensure => 'present', + } + MANIFEST + ensure_manifest_apply(host, pp) + end - it "Test Case C89121: create login with optional 'default_language'" do - #This test also cover test case C89125: create login with optional 'svrroles' - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - login_type => 'SQL_LOGIN', - password => '#{@login_passwd}', - svrroles => {'sysadmin' => 1}, - default_language => 'Spanish', - } - MANIFEST - ensure_manifest_apply(host, pp) - - puts "Validate the login '#{@login_user}' is successfully created and able to access database '#{db_name}':" - query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - - puts "Validate the login '#{@login_user}' is successfully created and has correct default_language_name:" - query = "SELECT name as LOGIN_NAME, default_language_name - FROM SYS.SQL_LOGINS - WHERE default_language_name = 'Spanish' - AND name = '#{@login_user}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - end + # Delete all test fixtures + after(:all) do + remove_test_logins(host) + + pp = <<-MANIFEST + sqlserver::config{'MSSQLSERVER': + admin_user => 'sa', + admin_pass => 'Pupp3t1@', + } + sqlserver::database{'#{db_name}': + instance => 'MSSQLSERVER', + ensure => 'absent', + } + user {'#{@windows_user}': + ensure => 'absent', + } + group {'#{@windows_group}': + ensure => 'absent', + } + MANIFEST + ensure_manifest_apply(host, pp) + end + + ['SQL_LOGIN user', 'WINDOWS_LOGIN user', 'WINDOWS_LOGIN group'].each do |testcase| + context "#{testcase} tests" do + before(:all) do + case testcase + when 'SQL_LOGIN user' + @login_under_test = @login_user + @sql_principal_type = 'S' + when 'WINDOWS_LOGIN user' + @login_under_test = @login_windows_user + @sql_principal_type = 'U' + when 'WINDOWS_LOGIN group' + @login_under_test = @login_windows_group + @sql_principal_type = 'G' + else + raise "Unknown testcase name #{testcase}" + end + end + + describe "Create deafult #{testcase} login" do + before(:all) { remove_test_logins(host) } + + it "can create a default #{testcase}" do + pp = create_login_manifest(testcase,@login_under_test,@login_passwd) + ensure_manifest_apply(host, pp) + end + + it "should exist in the principals table" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + if testcase == 'SQL_LOGIN user' + it "can login to SQL Server" do + puts "Validate the login '#{@login_under_test}' is successfully created and able to access database '#{db_name}':" + query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" + run_sql_query(host, run_sql_query_opts(@login_under_test, @login_passwd, query, expected_row_count = 1)) + end + end + + it "is idempotent" do + pp = create_login_manifest(testcase,@login_under_test,@login_passwd) + ensure_manifest_apply(host, pp) + end + end + + describe "Create #{testcase} login with 'check_expiration','check_policy'", :if => testcase == 'SQL_LOGIN user' do + # check_expiration and check_policy are only applicable to SQL based logins + + before(:all) { remove_test_logins(host) } + it "can create an #{testcase} with 'check_expiration','check_policy' set" do + options = { 'check_expiration' => true, 'check_policy' => true } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end + + it "should have is_expiration_checked set" do + query = "SELECT name as LOGIN_NAME, is_expiration_checked FROM SYS.SQL_LOGINS WHERE is_expiration_checked = '1' AND name = '#{@login_under_test}';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have is_policy_checked set" do + query = "SELECT name as LOGIN_NAME, is_policy_checked FROM SYS.SQL_LOGINS WHERE is_policy_checked = '1' AND name = '#{@login_under_test}';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + end + + describe "Create #{testcase} login with 'default_database','default_language'" do + before(:all) { remove_test_logins(host) } + + it "can create a #{testcase} with 'default_database','default_language'" do + options = { 'default_database' => "#{db_name}", 'default_language' => 'Spanish' } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end + + it "should exist in the principals table" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have the specified default database" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_database_name = '#{db_name}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have the specified default langauge" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_language_name = 'Spanish'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + end + + describe "Create #{testcase} login with 'disabled'" do + before(:all) { remove_test_logins(host) } + + it "can create #{testcase} with optional 'disabled'" do + options = { 'disabled' => true } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end + + if testcase == 'WINDOWS_LOGIN group' + it "should have DENY CONNECT SQL set" do + query = "SELECT sp.[state] FROM sys.server_principals p + INNER JOIN sys.server_permissions sp ON p.principal_id = sp.grantee_principal_id + WHERE sp.permission_name = 'CONNECT SQL' AND sp.class = 100 AND sp.state = 'D' AND p.name = '#{@login_under_test}' AND p.[type] = '#{@sql_principal_type}'" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + else + it "should have is_disabled set" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND is_disabled = '1';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + end + end + + describe "Modify a #{testcase} login" do + before(:all) { remove_test_logins(host) } + + it "should create an initial #{testcase}" do + options = { 'svrroles' => '{\'sysadmin\' => 1}' } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end + + it "should exist in the principals table on creation" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should modify a #{testcase} login" do + options = { 'disabled' => true, 'default_database' => "#{db_name}", 'default_language' => 'Spanish', 'check_expiration' => true, 'check_policy' => true, 'svrroles' => '{\'sysadmin\' => 1, \'serveradmin\' => 1}' } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end + + if testcase == 'SQL_LOGIN user' + it "should have is_expiration_checked set" do + query = "SELECT name as LOGIN_NAME, is_expiration_checked FROM SYS.SQL_LOGINS WHERE is_expiration_checked = '1' AND name = '#{@login_under_test}';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have is_policy_checked set" do + query = "SELECT name as LOGIN_NAME, is_policy_checked FROM SYS.SQL_LOGINS WHERE is_policy_checked = '1' AND name = '#{@login_under_test}';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + end + + it "should have the specified sysadmin role" do + query = "SELECT 'is_sysadmin' AS result WHERE IS_SRVROLEMEMBER('sysadmin','#{@login_under_test}') = 1" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + it "should have the specified serveradmin role" do + query = "SELECT 'is_serveradmin' AS result WHERE IS_SRVROLEMEMBER('serveradmin','#{@login_under_test}') = 1" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have the specified default database" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_database_name = '#{db_name}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should have the specified default langauge" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND default_language_name = 'Spanish'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + if testcase == 'WINDOWS_LOGIN group' + it "should have DENY CONNECT SQL set" do + query = "SELECT sp.[state] FROM sys.server_principals p + INNER JOIN sys.server_permissions sp ON p.principal_id = sp.grantee_principal_id + WHERE sp.permission_name = 'CONNECT SQL' AND sp.class = 100 AND sp.state = 'D' AND p.name = '#{@login_under_test}' AND p.[type] = '#{@sql_principal_type}'" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + else + it "should have is_disabled set" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}' AND is_disabled = '1';" + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + end + end + + describe "Delete #{testcase} login" do + before(:all) { remove_test_logins(host) } + + it "should create an initial #{testcase}" do + pp = create_login_manifest(testcase,@login_under_test,@login_passwd) + ensure_manifest_apply(host, pp) + end + + it "should exist in the principals table on creation" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 1)) + end + + it "should remove a #{testcase} on ensure => absent" do + options = { 'ensure' => 'absent' } + pp = create_login_manifest(testcase,@login_under_test,@login_passwd,options) + ensure_manifest_apply(host, pp) + end - #Temporarily skip this test because of ticket MODULES-2305 - xit "Test Case C89122: create login with optional 'disabled'" do - pp = <<-MANIFEST - sqlserver::config{'MSSQLSERVER': - admin_user => 'sa', - admin_pass => 'Pupp3t1@', - } - sqlserver::login{'#{@login_user}': - instance => 'MSSQLSERVER', - login_type => 'SQL_LOGIN', - password => '#{@login_passwd}', - svrroles => {'sysadmin' => 1}, - disabled => true, - } - MANIFEST - ensure_manifest_apply(host, pp) - - puts "Validate the login '#{@login_user}' is successfully created and able to access database '#{db_name}':" - query = "USE #{db_name}; SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE table_name = '#{table_name}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) - - puts "Validate the login '#{@login_user}' is successfully created and has correct is_disabled:" - query = "SELECT name as LOGIN_NAME, is_policy_checked - FROM SYS.SQL_LOGINS - WHERE is_disabled = '1' - AND name = '#{@login_user}';" - run_sql_query(host, run_sql_query_opts(@login_user, @login_passwd, query, expected_row_count = 1)) + it "should not exist in the principals table after deletion" do + query = "SELECT principal_id FROM SYS.server_principals WHERE name = '#{@login_under_test}' AND [type] = '#{@sql_principal_type}'"; + run_sql_query(host, run_sql_query_opts_as_sa(query, expected_row_count = 0)) + end + end end end end diff --git a/spec/defines/login_spec.rb b/spec/defines/login_spec.rb index 72faad20..56c49206 100644 --- a/spec/defines/login_spec.rb +++ b/spec/defines/login_spec.rb @@ -18,8 +18,7 @@ end describe 'parameter assignment' do let(:should_contain_command) { [ - "IF exists(select * from sys.sql_logins where name = 'myTitle')", - "@login as varchar(255) = 'myTitle'", + "FROM sys.server_principals where name = 'myTitle'", '@is_disabled as tinyint = 0' ] } let(:should_contain_onlyif) { [ @@ -30,7 +29,7 @@ "@type_desc as varchar(50) = 'SQL_LOGIN'", "@default_db as varchar(255) = 'master'", "@default_lang as varchar(50) = 'us_english'", - "IF NOT EXISTS(SELECT name FROM sys.server_principals WHERE name = 'myTitle')" + "FROM sys.server_principals WHERE name = 'myTitle'" ] } it_behaves_like 'sqlserver_tsql command' it_behaves_like 'sqlserver_tsql onlyif' diff --git a/templates/create/login.sql.erb b/templates/create/login.sql.erb index 255f9c7e..c29f2291 100644 --- a/templates/create/login.sql.erb +++ b/templates/create/login.sql.erb @@ -1,32 +1,11 @@ DECLARE - @login as varchar(255) = '<%= @login %>', - @is_disabled as tinyint = <%= @disabled ? 1 : 0 %>; -IF exists(select * from sys.sql_logins where name = '<%= @login %>') -BEGIN - IF @is_disabled = 0 ALTER LOGIN [<%= @login %>] ENABLE - ELSE ALTER LOGIN [<%= @login %>] DISABLE + @is_disabled as tinyint = <%= @disabled ? 1 : 0 %>, + @login_type as varchar(255) = NULL; - ALTER LOGIN [<%= @login %>] WITH - <% if @login_type != 'WINDOWS_LOGIN' -%> - CHECK_EXPIRATION = <% if @check_expiration %>ON<% else %>OFF<% end %>, - CHECK_POLICY = <% if @check_policy %>ON<% else %>OFF<% end %>, - <% end -%> - DEFAULT_LANGUAGE = [<%= @default_language %>], - DEFAULT_DATABASE = [<%= @default_database %>]; - <% @svrroles.each do |role, enable_bit| -%> - IF IS_SRVROLEMEMBER('<%= role %>','<%= @login %>') != <%= enable_bit %> - BEGIN - <% if enable_bit == '1' || enable_bit == 1 -%> - ALTER SERVER ROLE <%= role %> ADD MEMBER [<%= @login %>]; - <% else -%> - ALTER SERVER ROLE <%= role %> DROP MEMBER [<%= @login %>]; - <% end -%> - END - <% end -%> - -END -ELSE +SET @login_type = (SELECT [type] FROM sys.server_principals where name = '<%= @login %>') +IF (@login_type IS NULL) BEGIN + -- Create the login CREATE LOGIN [<%= @login %>] <% if @login_type !~ /WINDOWS_LOGIN/i -%> WITH @@ -38,12 +17,37 @@ BEGIN <% end -%> DEFAULT_LANGUAGE = [<%= @default_language %>], DEFAULT_DATABASE = [<%= @default_database %>]; - <% @svrroles.each do |role, enable_bit| -%> - <% if enable_bit == '1' || enable_bit == 1 -%> - ALTER SERVER ROLE <%= role %> ADD MEMBER [<%= @login %>]; - <% else -%> - ALTER SERVER ROLE <%= role %> DROP MEMBER [<%= @login %>]; - <% end -%> - <% end -%> + -- Fetch the login type + SET @login_type = (SELECT [type] FROM sys.server_principals where name = '<%= @login %>') END +IF (@login_type = 'G') +BEGIN + -- Windows Group type logins can only be granted/denied connection + IF @is_disabled = 0 GRANT CONNECT SQL TO [<%= @login %>] + ELSE DENY CONNECT SQL TO [<%= @login %>] +END +ELSE +BEGIN + IF @is_disabled = 0 ALTER LOGIN [<%= @login %>] ENABLE + ELSE ALTER LOGIN [<%= @login %>] DISABLE +END + +ALTER LOGIN [<%= @login %>] WITH +<% if @login_type != 'WINDOWS_LOGIN' -%> + CHECK_EXPIRATION = <% if @check_expiration %>ON<% else %>OFF<% end %>, + CHECK_POLICY = <% if @check_policy %>ON<% else %>OFF<% end %>, +<% end -%> + DEFAULT_LANGUAGE = [<%= @default_language %>], + DEFAULT_DATABASE = [<%= @default_database %>]; + +<% @svrroles.each do |role, enable_bit| -%> +IF IS_SRVROLEMEMBER('<%= role %>','<%= @login %>') != <%= enable_bit %> +BEGIN + <% if enable_bit == '1' || enable_bit == 1 -%> + ALTER SERVER ROLE [<%= role %>] ADD MEMBER [<%= @login %>]; + <% else -%> + ALTER SERVER ROLE [<%= role %>] DROP MEMBER [<%= @login %>]; + <% end -%> +END +<% end -%> diff --git a/templates/delete/database.sql.erb b/templates/delete/database.sql.erb index 99f893db..37d2c6ed 100644 --- a/templates/delete/database.sql.erb +++ b/templates/delete/database.sql.erb @@ -1,11 +1,11 @@ +USE [master]; + /* Delete Database Backup and Restore History from MSDB System Database */ -EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'<%= @db_name %>' -GO +EXEC msdb.dbo.sp_delete_database_backuphistory @database_name = N'<%= @db_name %>'; + /* Query to Get Exclusive Access of SQL Server Database before Dropping the Database */ -USE [master] -GO -ALTER DATABASE [<%= @db_name %>] SET SINGLE_USER WITH ROLLBACK IMMEDIATE -GO +ALTER DATABASE [<%= @db_name %>] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; + /* Query to Drop Database in SQL Server */ -DROP DATABASE [<%= @db_name %>] -GO +DROP DATABASE [<%= @db_name %>]; + diff --git a/templates/delete/login.sql.erb b/templates/delete/login.sql.erb index 202b9e46..df8edac4 100644 --- a/templates/delete/login.sql.erb +++ b/templates/delete/login.sql.erb @@ -1,8 +1,7 @@ -USE master -GO -IF exists(select * from sys.sql_logins where name = '<%= @login %>') +USE master; +IF exists(select * from sys.server_principals where name = '<%= @login %>') BEGIN -- need to add logic to kill all possible connections if any exists, -- possible force flag to prevent from happening during transaction if user would prefer to wait - execute sp_droplogin @loginame = [<%= @login %>] + DROP LOGIN [<%= @login %>] END diff --git a/templates/query/login_exists.sql.erb b/templates/query/login_exists.sql.erb index 08716060..69df8a39 100644 --- a/templates/query/login_exists.sql.erb +++ b/templates/query/login_exists.sql.erb @@ -1,40 +1,65 @@ DECLARE @login as varchar(255) = '<%= @login %>', @is_disabled as tinyint = <%= @disabled ? 1 : 0 %>, + @connect_sql_perm as char(1) = '<%= @disabled ? 'D' : 'G' %>', @check_expiration as tinyint = <%= @check_expiration ? 1 : 0 %>, @check_policy as tinyint = <%= @check_policy ? 1 : 0 %>, @type_desc as varchar(50) = '<%= @login_type %>', @default_db as varchar(255) = '<%= @default_database %>', - @default_lang as varchar(50) = '<%= @default_language %>' -IF <% if @ensure == 'present' %>NOT<% end %> EXISTS(SELECT name FROM sys.server_principals WHERE name = '<%= @login %>') + @default_lang as varchar(50) = '<%= @default_language %>', + @principal_type as varchar(255) = NULL; + +IF <% if @ensure == 'present' %>NOT<% end %> EXISTS(SELECT name FROM sys.server_principals WHERE name = '<%= @login %>') THROW 51000, 'ERROR: The login is not <%= @ensure %>', 10 <% if @ensure == 'present' %> BEGIN -IF NOT EXISTS( - SELECT p.name FROM sys.server_principals p - LEFT JOIN sys.sql_logins s ON s.name = p.name - WHERE - p.name = @login - AND p.type_desc = @type_desc - AND p.is_disabled = @is_disabled - AND p.default_database_name = @default_db - AND p.default_language_name = @default_lang - <% if @login_type == 'SQL_LOGIN' %> - AND is_policy_checked = @check_policy - AND is_expiration_checked = @check_expiration - <% end %> - ) THROW 51000, 'ERROR: The login is not in the correct state', 10 - /* If it does exist check for each role is in the correct state */ - <% @svrroles.each do |role, enable_bit| %> - IF (SELECT COUNT(me.role_principal_id) from sys.server_role_members me - JOIN sys.server_principals rol ON me.role_principal_id = rol.principal_id - JOIN sys.server_principals pri ON me.member_principal_id = pri.principal_id - WHERE rol.type_desc = 'SERVER_ROLE' - AND rol.name = '<%= role %>' - AND pri.name = '<%= @login %>') != <%= enable_bit %> - THROW 51000, 'ERROR: a role is not correct for <%= role %>', 10 - <% end %> +/* Check if account exists in the correct state */ +SET @principal_type = (SELECT p.[type] FROM sys.server_principals p + LEFT JOIN sys.sql_logins s ON s.name = p.name + WHERE + p.name = @login + AND p.default_database_name = @default_db + AND p.default_language_name = @default_lang + -- Only check disabled status if it's not a WINDOWS_GROUP + AND (p.type_desc = 'WINDOWS_GROUP' OR p.is_disabled = @is_disabled) + <% if @login_type == 'SQL_LOGIN' %> + AND s.is_policy_checked = @check_policy + AND s.is_expiration_checked = @check_expiration + <% end %> + <% if @login_type == 'WINDOWS_LOGIN' %> + AND ((p.type_desc = 'WINDOWS_LOGIN') OR (p.type_desc = 'WINDOWS_GROUP')) + <% else %> + AND p.type_desc = @type_desc + <% end %> + ) +IF (@principal_type IS NULL) THROW 51000, 'ERROR: The login is not in the correct state', 10 + +<% if @login_type == 'WINDOWS_LOGIN' %> +/* Look for the CONNECT SQL server permission on the WINDOWS_GROUP */ +IF (@principal_type = 'G') +BEGIN + IF NOT EXISTS(SELECT sp.[state] FROM sys.server_principals p + INNER JOIN sys.server_permissions sp ON p.principal_id = sp.grantee_principal_id + WHERE + sp.permission_name = 'CONNECT SQL' + AND sp.class = 100 + AND p.name = @login + AND sp.state = @connect_sql_perm + ) THROW 51000, 'ERROR: The group login is not in the correct state', 10 +END +<% end %> + +/* If it does exist check for each role is in the correct state */ +<% @svrroles.each do |role, enable_bit| %> + IF (SELECT COUNT(me.role_principal_id) from sys.server_role_members me + JOIN sys.server_principals rol ON me.role_principal_id = rol.principal_id + JOIN sys.server_principals pri ON me.member_principal_id = pri.principal_id + WHERE rol.type_desc = 'SERVER_ROLE' + AND rol.name = '<%= role %>' + AND pri.name = '<%= @login %>') != <%= enable_bit %> + THROW 51000, 'ERROR: a role is not correct for <%= role %>', 10 +<% end %> <% end %> END