Skip to content

Commit 0b97c1d

Browse files
author
Travis Fields
committed
FM-1556 Add ability to manage login server level permissions
1 parent 5249370 commit 0b97c1d

File tree

5 files changed

+212
-0
lines changed

5 files changed

+212
-0
lines changed

manifests/login/permission.pp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
##
2+
# == Define Resource Type: sqlserver::login::permission#
3+
#
4+
# === Requirement/Dependencies:
5+
#
6+
# Requires defined type {sqlserver::config} in order to execute against the SQL Server instance
7+
#
8+
#
9+
# === Parameters
10+
# [login]
11+
# The login for which the permission will be manage.
12+
#
13+
# [permission]
14+
# The permission you would like managed. i.e. 'SELECT', 'INSERT', 'UPDATE', 'DELETE'
15+
#
16+
# [state]
17+
# The state you would like the permission in. Accepts 'GRANT', 'DENY', 'REVOKE' Please note that REVOKE equates to absent and will default to database and system level permissions.
18+
#
19+
# [instance]
20+
# The name of the instance where the user and database exists. Defaults to 'MSSQLSERVER'
21+
#
22+
##
23+
define sqlserver::login::permission (
24+
$login,
25+
$permission = $title,
26+
$state = 'GRANT',
27+
$with_grant_option = false,
28+
$instance = 'MSSQLSERVER',
29+
){
30+
sqlserver_validate_instance_name($instance)
31+
32+
## Validate Permissions
33+
$_permission = upcase($permission)
34+
sqlserver_validate_range($_permission, 4, 128, 'Permission must be between 4 and 128 characters')
35+
validate_re($_permission, '^([A-Z]|\s)+$', 'Permissions must be alphabetic only')
36+
37+
sqlserver_validate_range($login, 1, 128, 'Login must be between 1 and 128 characters')
38+
39+
## Validate state
40+
$_state = upcase($state)
41+
validate_re($_state,'^(GRANT|REVOKE|DENY)$', "State parameter can only be one of 'GRANT', 'REVOKE' or 'DENY', you passed a value of ${state}")
42+
43+
validate_bool($with_grant_option)
44+
45+
sqlserver_tsql{ "login-permission-${instance}-${login}-${_permission}":
46+
instance => $instance,
47+
command => template('sqlserver/create/login/permission.sql.erb'),
48+
onlyif => template('sqlserver/query/login/permission_exists.sql.erb'),
49+
require => Sqlserver::Config[$instance],
50+
}
51+
}

spec/defines/login/permission_spec.rb

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
require 'spec_helper'
2+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'manifest_shared_examples.rb'))
3+
4+
describe 'sqlserver::login::permission' do
5+
let(:facts) { {:osfamily => 'windows'} }
6+
context 'validation errors' do
7+
include_context 'manifests' do
8+
let(:title) { 'myTitle' }
9+
let(:sqlserver_tsql_title) { 'login-permission-MSSQLSERVER-loggingUser-SELECT' }
10+
end
11+
context 'login =>' do
12+
let(:params) { {
13+
:permission => 'SELECT',
14+
} }
15+
let(:raise_error_check) { 'Login must be between 1 and 128 characters' }
16+
describe 'missing' do
17+
let(:raise_error_check) { 'Must pass login to Sqlserver::Login::Permission[myTitle]' }
18+
it_behaves_like 'validation error'
19+
end
20+
describe 'empty' do
21+
let(:additional_params) { {:login => ''} }
22+
it_behaves_like 'validation error'
23+
end
24+
describe 'over limit' do
25+
let(:additional_params) { {:login => random_string_of_size(129)} }
26+
it_behaves_like 'validation error'
27+
end
28+
end
29+
context 'permission' do
30+
let(:params) { {
31+
:login => 'loggingUser',
32+
} }
33+
let(:raise_error_check) { 'Permission must be between 4 and 128 characters' }
34+
describe 'empty' do
35+
let(:additional_params) { {:permission => ''} }
36+
it_behaves_like 'validation error'
37+
end
38+
describe 'under limit' do
39+
let(:additional_params) { {:permission => random_string_of_size(3, false)} }
40+
it_behaves_like 'validation error'
41+
end
42+
describe 'over limit' do
43+
let(:additional_params) { {:permission => random_string_of_size(129, false)} }
44+
it_behaves_like 'validation error'
45+
end
46+
end
47+
context 'state =>' do
48+
let(:params) { {
49+
:permission => 'SELECT',
50+
:login => 'loggingUser'
51+
} }
52+
describe 'invalid' do
53+
let(:additional_params) { {:state => 'invalid'} }
54+
let(:raise_error_check) { "State parameter can only be one of 'GRANT', 'REVOKE' or 'DENY', you passed a value of invalid" }
55+
it_behaves_like 'validation error'
56+
end
57+
end
58+
end
59+
context 'successfully' do
60+
include_context 'manifests' do
61+
let(:title) { 'myTitle' }
62+
let(:sqlserver_tsql_title) { 'login-permission-MSSQLSERVER-loggingUser-SELECT' }
63+
let(:params) { {
64+
:login => 'loggingUser',
65+
:permission => 'SELECT',
66+
} }
67+
end
68+
%w(revoke grant deny).each do |state|
69+
context "state => '#{state}'" do
70+
let(:sqlserver_tsql_title) { "login-permission-MSSQLSERVER-loggingUser-SELECT" }
71+
let(:should_contain_command) { ["#{state.upcase} SELECT TO [loggingUser];", 'USE [master];'] }
72+
describe "lowercase #{state}" do
73+
let(:additional_params) { {:state => state} }
74+
it_behaves_like 'sqlserver_tsql command'
75+
end
76+
state.capitalize!
77+
describe "capitalized #{state}" do
78+
let(:additional_params) { {:state => state} }
79+
it_behaves_like 'sqlserver_tsql command'
80+
end
81+
end
82+
end
83+
84+
context 'permission' do
85+
describe 'upper limit' do
86+
permission =random_string_of_size(128, false)
87+
let(:additional_params) { {:permission => permission} }
88+
let(:sqlserver_tsql_title) { "login-permission-MSSQLSERVER-loggingUser-#{permission.upcase}" }
89+
let(:should_contain_command) { ['USE [master];'] }
90+
it_behaves_like 'sqlserver_tsql command'
91+
end
92+
describe 'alter' do
93+
let(:additional_params) { {:permission => 'ALTER'} }
94+
let(:should_contain_command) { ['USE [master];', 'GRANT ALTER TO [loggingUser];'] }
95+
let(:sqlserver_tsql_title) { "login-permission-MSSQLSERVER-loggingUser-ALTER" }
96+
it_behaves_like 'sqlserver_tsql command'
97+
end
98+
end
99+
100+
describe 'Minimal Params' do
101+
let(:pre_condition) { <<-EOF
102+
define sqlserver::config{}
103+
sqlserver::config {'MSSQLSERVER': }
104+
EOF
105+
}
106+
it_behaves_like 'compile'
107+
end
108+
109+
end
110+
111+
context 'command syntax' do
112+
include_context 'manifests' do
113+
let(:title) { 'myTitle' }
114+
let(:sqlserver_tsql_title) { 'login-permission-MSSQLSERVER-loggingUser-SELECT' }
115+
let(:params) { {
116+
:login => 'loggingUser',
117+
:permission => 'SELECT',
118+
} }
119+
describe '' do
120+
let(:should_contain_command) { [
121+
'USE [master];',
122+
'GRANT SELECT TO [loggingUser];',
123+
/DECLARE @perm_state varchar\(250\)/,
124+
/SET @perm_state = ISNULL\(\n\s+\(SELECT perm.state_desc FROM sys\.server_permissions perm\n\s+JOIN sys\./,
125+
/JOIN sys\.server_principals princ ON princ.principal_id = perm\.grantee_principal_id\n\s+WHERE/,
126+
/WHERE princ\.type IN \('U','S','G'\)\n\s+ AND princ\.name = 'loggingUser'\n\s+AND perm\.permission_name = 'SELECT'\),\n\s+'REVOKE'\)/,
127+
/DECLARE @error_msg varchar\(250\);\nSET @error_msg = 'EXPECTED login \[loggingUser\] to have permission \[SELECT\] with GRANT but got ' \+ @perm_state;/,
128+
/IF @perm_state != 'GRANT'\n\s+THROW 51000, @error_msg, 10/
129+
] }
130+
it_behaves_like 'sqlserver_tsql command'
131+
end
132+
end
133+
end
134+
135+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
BEGIN
2+
USE [master];
3+
<% if @with_grant_option == false %>
4+
IF 'GRANT_WITH_GRANT_OPTION' = <%= scope.function_template(['sqlserver/snippets/login/get_perm_state.sql.erb']) %>
5+
REVOKE GRANT OPTION FOR <%= @_permission %> TO [<%= @login %>] CASCADE;
6+
<% end %>
7+
<%= @_state %> <%= @_permission %> TO [<%= @login %>]<% if @with_grant_option == true %> WITH GRANT OPTION<% end %>;
8+
END
9+
BEGIN
10+
<%= scope.function_template(['sqlserver/query/login/permission_exists.sql.erb']) %>
11+
END
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
USE [master];
2+
DECLARE @perm_state varchar(250);
3+
SET @perm_state = <%= scope.function_template(['sqlserver/snippets/login/get_perm_state.sql.erb']) %>;
4+
DECLARE @error_msg varchar(250);
5+
SET @error_msg = 'EXPECTED login [<%= @login %>] to have permission [<%= @_permission %>] with <%= @_state %> but got ' + @perm_state;
6+
7+
IF @perm_state != '<% if @with_grant_option == true %>GRANT_WITH_GRANT_OPTION<% else %><%= @_state %><% end %>'
8+
THROW 51000, @error_msg, 10
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ISNULL(
2+
(SELECT perm.state_desc FROM sys.server_permissions perm
3+
JOIN sys.server_principals princ ON princ.principal_id = perm.grantee_principal_id
4+
WHERE princ.type IN ('U','S','G')
5+
AND princ.name = '<%= @login %>'
6+
AND perm.permission_name = '<%= @_permission %>'),
7+
'REVOKE')

0 commit comments

Comments
 (0)