zepplen_aws 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +57 -2
- data/bin/test.yaml +19 -0
- data/bin/zepplen_users +49 -0
- data/bin/zepplen_users_admin +257 -0
- data/lib/zepplen_aws.rb +50 -0
- data/lib/zepplen_aws/auto_dns.rb +2 -0
- data/lib/zepplen_aws/aws.rb +4 -0
- data/lib/zepplen_aws/aws/dynamo_db.rb +29 -0
- data/lib/zepplen_aws/aws/instance_data.rb +76 -0
- data/lib/zepplen_aws/aws/s3.rb +29 -0
- data/lib/zepplen_aws/env.rb +10 -2
- data/lib/zepplen_aws/exceptions.rb +6 -0
- data/lib/zepplen_aws/exceptions/base.rb +6 -0
- data/lib/zepplen_aws/exceptions/users.rb +10 -0
- data/lib/zepplen_aws/server_local_users.rb +256 -0
- data/lib/zepplen_aws/server_user.rb +395 -0
- data/lib/zepplen_aws/server_users.rb +259 -0
- metadata +20 -6
@@ -0,0 +1,395 @@
|
|
1
|
+
#Copyright 2013 Mark Trimmer
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module ZepplenAWS
|
16
|
+
|
17
|
+
# = Manage Server User
|
18
|
+
#
|
19
|
+
# This class is intended to be used by both the CLI scripts provided, and 3rd party tools
|
20
|
+
# written by you!
|
21
|
+
#
|
22
|
+
class ServerUser
|
23
|
+
|
24
|
+
# = ServerUser
|
25
|
+
# If you are using the default DynamoDB table name 'users' only user_name is required to create
|
26
|
+
# a ServerUser object. If you have deviated from this standard you must pass dynamo_table name in
|
27
|
+
# as either a paramater, or via the Env class.
|
28
|
+
#
|
29
|
+
# @param [String] User Name
|
30
|
+
# @param optional [String] Name of DynamoDB to read settings and users from
|
31
|
+
# @param optional [AWS::DynamoDB::Item] DynamoDB Item reflecting the requested user (used to prevent multiple DB Hits)
|
32
|
+
# @param optional [AWS::DynamoDB::Item] DynamoDB Item reflecting the metadata setings (used to prevent multiple DB Hits)
|
33
|
+
# @param optional [ZepplenAWS::ServerUsers] ServerUsers object (used to prevent multiple DB Hits)
|
34
|
+
def initialize(user_name, dynamo_table = nil, user_row=nil, metadata_row=nil, server_users=nil)
|
35
|
+
@user_name = user_name
|
36
|
+
@user_data = {}
|
37
|
+
@remove_s3_files = []
|
38
|
+
@dirty = false
|
39
|
+
@marshaled_columns = [/^TAG__/, /^files$/]
|
40
|
+
|
41
|
+
@dynamo_table = dynamo_table || Env[:dynamo_table] || 'users'
|
42
|
+
|
43
|
+
if(@dynamo_table == nil)
|
44
|
+
raise Exceptions::Users::MissingOption, "DynamoDB Table Name Required"
|
45
|
+
end
|
46
|
+
|
47
|
+
@dynamo = AWS::DynamoDB.new()
|
48
|
+
@table = @dynamo.tables[@dynamo_table]
|
49
|
+
@table.hash_key = {:type => :string}
|
50
|
+
@table.range_key = {:user_name => :string}
|
51
|
+
if(user_row != nil && user_row.attributes[:user_name] == user_name)
|
52
|
+
@user_row = user_row
|
53
|
+
else
|
54
|
+
@user_row = @table.items['USER', @user_name]
|
55
|
+
end
|
56
|
+
get_user_data(@user_row.attributes)
|
57
|
+
|
58
|
+
if(metadata_row != nil && metadata_row.attributes[:user_name] == @metadata_label)
|
59
|
+
@metadata_row = metadata_row
|
60
|
+
else
|
61
|
+
@metadata_row = @table.items['METADATA', '__metadata__']
|
62
|
+
end
|
63
|
+
|
64
|
+
if(server_users != nil)
|
65
|
+
@server_users = server_users
|
66
|
+
else
|
67
|
+
@server_users = ServerUsers.new(@dynamo_table)
|
68
|
+
end
|
69
|
+
|
70
|
+
if(!@user_row.exists?())
|
71
|
+
init_user()
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# User Name
|
76
|
+
#
|
77
|
+
# @return [String]
|
78
|
+
def user_name()
|
79
|
+
return @user_name
|
80
|
+
end
|
81
|
+
|
82
|
+
# Hash of files associated with user's profile
|
83
|
+
#
|
84
|
+
# @return [Hash]
|
85
|
+
def files()
|
86
|
+
return @user_data['files']
|
87
|
+
end
|
88
|
+
|
89
|
+
# Remove file from user's profile
|
90
|
+
#
|
91
|
+
# Note: the file will be removed from S3 Before object is saved!
|
92
|
+
#
|
93
|
+
# @param [String] Remote file location of file to remove
|
94
|
+
def remove_file(file_name)
|
95
|
+
if(@user_data['files'].has_key?(file_name))
|
96
|
+
s3 = AWS::S3.new()
|
97
|
+
bucket = s3.buckets[@server_users.user_file_bucket]
|
98
|
+
bucket.objects[@user_data['files'][file_name]['s3_path']].delete()
|
99
|
+
@user_data['files'].delete(file_name)
|
100
|
+
end
|
101
|
+
@dirty = true
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# Add File Path
|
106
|
+
#
|
107
|
+
# Note: This will add the file to S3 Before the object is saved!
|
108
|
+
#
|
109
|
+
# @param [String] Location of file to add
|
110
|
+
# @param [String] Destination in the user's home dir to place file on remove servers
|
111
|
+
# @param [String] Linux file mode (eg: '600')
|
112
|
+
def add_file_path(local_path, remote_path, file_mode)
|
113
|
+
if(!File.readable?(local_path))
|
114
|
+
raise "Can Not Read #{local_path}"
|
115
|
+
end
|
116
|
+
data = File.read(local_path)
|
117
|
+
add_file_data(data, remote_path, file_mode)
|
118
|
+
return nil
|
119
|
+
end
|
120
|
+
|
121
|
+
# Add File Path
|
122
|
+
#
|
123
|
+
# Note: This will add the file to S3 Before the object is saved!
|
124
|
+
#
|
125
|
+
# @param [String] File data
|
126
|
+
# @param [String] Destination in the user's home dir to place file on remove servers
|
127
|
+
# @param [String] Linux file mode (eg: '600')
|
128
|
+
def add_file_data(data, remote_path, file_mode)
|
129
|
+
s3_path = @server_users.user_file_bucket()
|
130
|
+
if(!s3_path)
|
131
|
+
raise 'User Files are not enabled. Please re-run --configure'
|
132
|
+
end
|
133
|
+
s3 = AWS::S3.new()
|
134
|
+
bucket = s3.buckets[@server_users.user_file_bucket]
|
135
|
+
file_path = "#{@user_name}/#{remote_path.sub(/^\//, '')}"
|
136
|
+
bucket.objects[file_path].write(data)
|
137
|
+
file_size = bucket.objects[file_path].content_length.to_i
|
138
|
+
@user_data['files'][remote_path] = {'s3_path' => file_path, 'mode' => file_mode, 'content_length' => file_size}
|
139
|
+
@dirty = true
|
140
|
+
return nil
|
141
|
+
end
|
142
|
+
|
143
|
+
# Shell
|
144
|
+
#
|
145
|
+
# @return [String] User's shell
|
146
|
+
def shell()
|
147
|
+
return @user_data['shell']
|
148
|
+
end
|
149
|
+
|
150
|
+
# Set Shell
|
151
|
+
#
|
152
|
+
# @param [String] Set user's shell. (Default: /bin/bash)
|
153
|
+
def shell=(shell)
|
154
|
+
@user_data['shell'] = shell
|
155
|
+
@dirty = true
|
156
|
+
return nil
|
157
|
+
end
|
158
|
+
|
159
|
+
# User State
|
160
|
+
#
|
161
|
+
# @return [String] User State (ACTIVE/INACTIVE)
|
162
|
+
def state()
|
163
|
+
return @user_data['state']
|
164
|
+
end
|
165
|
+
|
166
|
+
# Set User State
|
167
|
+
#
|
168
|
+
# @param [String/Symbol] User State (ACTIVE/INACTIVE)
|
169
|
+
def state=(state)
|
170
|
+
@user_data['state'] = (state.upcase.to_s == 'ACTIVE' ? 'ACTIVE' : 'INACTIVE')
|
171
|
+
@dirty = true
|
172
|
+
return nil
|
173
|
+
end
|
174
|
+
|
175
|
+
# Public Key
|
176
|
+
#
|
177
|
+
# @return [String] SSH Public Key
|
178
|
+
def public_key()
|
179
|
+
return @user_data['public_key']
|
180
|
+
end
|
181
|
+
|
182
|
+
# Set Public Key
|
183
|
+
#
|
184
|
+
# Setting the public key also sets the public_key_expire date.
|
185
|
+
#
|
186
|
+
# @param [String] SSH Public Key
|
187
|
+
def public_key=(key)
|
188
|
+
if(@user_data['public_key'] != key)
|
189
|
+
@user_data['public_key'] = key
|
190
|
+
@user_data['public_key_expire'] = (Date.today + @server_users.max_key_age).to_s
|
191
|
+
@dirty = true
|
192
|
+
end
|
193
|
+
return nil
|
194
|
+
end
|
195
|
+
|
196
|
+
# Public Key Expire Date
|
197
|
+
#
|
198
|
+
# This is only set when the public_key value is set
|
199
|
+
#
|
200
|
+
# @return [String] Public Key Expire Date (String)
|
201
|
+
def public_key_expire()
|
202
|
+
return @user_data['public_key_expire']
|
203
|
+
end
|
204
|
+
|
205
|
+
# Full Name
|
206
|
+
#
|
207
|
+
# @return [String] User's full name
|
208
|
+
def full_name()
|
209
|
+
return @user_data['full_name']
|
210
|
+
end
|
211
|
+
|
212
|
+
# Set Full Name
|
213
|
+
#
|
214
|
+
# @param [String] User's full name
|
215
|
+
def full_name=(name)
|
216
|
+
if(@user_data['full_name'] != name)
|
217
|
+
@user_data['full_name'] = name
|
218
|
+
@dirty = true
|
219
|
+
end
|
220
|
+
return nil
|
221
|
+
end
|
222
|
+
|
223
|
+
# User ID
|
224
|
+
#
|
225
|
+
# Linux User ID (set automaticaly at user creation)
|
226
|
+
#
|
227
|
+
# @return [Integer] Linux user id
|
228
|
+
def user_id()
|
229
|
+
return @user_data['user_id']
|
230
|
+
end
|
231
|
+
|
232
|
+
# Identity
|
233
|
+
#
|
234
|
+
# Used to identify if the user entry has changed. Incremented on each write to DynamoDB for this user.
|
235
|
+
#
|
236
|
+
# @return [Integer] Identity
|
237
|
+
def identity()
|
238
|
+
return @user_data['identity']
|
239
|
+
end
|
240
|
+
|
241
|
+
# Remove Access
|
242
|
+
#
|
243
|
+
# Remove's a users access from a Tag Name => Tag Value combination
|
244
|
+
#
|
245
|
+
# @param [String] EC2 Tag Name to target (Case Sensitive)
|
246
|
+
# @param [String] EC2 Tag Value to target (Case Sensitive)
|
247
|
+
def remove_access(tag_name, tag_value)
|
248
|
+
if(!@metadata_row.attributes[:tags].include?(tag_name))
|
249
|
+
raise "User Access Tag Name #{tag_name} Not In [#{@metadata_row.attributes[:tags].to_a.join(', ')}]"
|
250
|
+
end
|
251
|
+
tag_key_name = "TAG__#{tag_name}"
|
252
|
+
if(!@user_data.has_key?(tag_key_name))
|
253
|
+
return
|
254
|
+
end
|
255
|
+
if(!@user_data[tag_key_name].has_key?(tag_value))
|
256
|
+
return
|
257
|
+
end
|
258
|
+
@user_data[tag_key_name].delete(tag_value)
|
259
|
+
@dirty = true
|
260
|
+
return nil
|
261
|
+
end
|
262
|
+
|
263
|
+
# Add Access
|
264
|
+
#
|
265
|
+
# Users are targeted to a server by EC2 Tags. The list of valid tags is listed at the environmen level.
|
266
|
+
# run --configure to change the list of valid tags
|
267
|
+
#
|
268
|
+
# @param [String] EC2 Tag Name
|
269
|
+
# @param [String] EC2 Tag Value
|
270
|
+
# @param [Object] Grant Sudo access. (nil: no sudo, not_nil: sudo access)
|
271
|
+
def add_access(tag_name, tag_value, sudo)
|
272
|
+
if(!@metadata_row.attributes[:tags].include?(tag_name))
|
273
|
+
raise "User Access Tag Name #{tag_name} Not In [#{@metadata_row.attributes[:tags].to_a.join(', ')}]"
|
274
|
+
end
|
275
|
+
tag_key_name = "TAG__#{tag_name}"
|
276
|
+
if(!@user_data.has_key?(tag_key_name))
|
277
|
+
@user_data[tag_key_name] = {}
|
278
|
+
end
|
279
|
+
value_data = {'sudo' => sudo != nil}
|
280
|
+
if(@user_data[tag_key_name][tag_value] != value_data)
|
281
|
+
@user_data[tag_key_name][tag_value] = value_data
|
282
|
+
@dirty = true
|
283
|
+
end
|
284
|
+
return nil
|
285
|
+
end
|
286
|
+
|
287
|
+
# Save
|
288
|
+
#
|
289
|
+
# This call will only write to Dynamo if a change has been made.
|
290
|
+
# When we update the row we also update the identity of the row. This will allows us to save
|
291
|
+
# calls to Dynamo/S3 when we write the users to the remote servers
|
292
|
+
def save()
|
293
|
+
if(!@dirty)
|
294
|
+
return
|
295
|
+
end
|
296
|
+
@user_row.attributes.update do |u|
|
297
|
+
@user_data.each_pair do |key,value|
|
298
|
+
if(key != 'user_name' && key != 'identity' && key != 'type')
|
299
|
+
if(value == nil)
|
300
|
+
u.delete(key)
|
301
|
+
else
|
302
|
+
marshal = false
|
303
|
+
@marshaled_columns.each do |regex|
|
304
|
+
if(key.match(regex))
|
305
|
+
marshal = true
|
306
|
+
break
|
307
|
+
end
|
308
|
+
end
|
309
|
+
if(marshal)
|
310
|
+
value = value.to_json.force_encoding('UTF-8')
|
311
|
+
end
|
312
|
+
u.set(key => value)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
if(!@user_data['user_id'])
|
317
|
+
user_id = @metadata_row.attributes.add({:next_uid => 1}, :return => :updated_old)
|
318
|
+
u.set(:user_id => user_id['next_uid'])
|
319
|
+
end
|
320
|
+
u.add(:identity => 1)
|
321
|
+
end
|
322
|
+
@metadata_row.attributes.add(:identity => 1)
|
323
|
+
@dirty = false
|
324
|
+
return nil
|
325
|
+
end
|
326
|
+
|
327
|
+
# Display User
|
328
|
+
#
|
329
|
+
# Prints the user's profile to the screen with console colors enabled
|
330
|
+
def display()
|
331
|
+
puts "Full Name: ".green()+"#{@user_data['full_name']}".light_blue
|
332
|
+
puts "UserName: ".green()+"#{@user_name}".light_blue
|
333
|
+
puts "State: ".green()+"#{@user_data['state']}".send(@user_data['state'] == 'ACTIVE' ? :light_blue : :red)
|
334
|
+
puts "Key Expire: ".green()+"#{@user_data['public_key_expire']}".light_blue
|
335
|
+
puts "Shell: ".green()+"#{@user_data['shell']}".light_blue
|
336
|
+
puts
|
337
|
+
puts "User Access".green
|
338
|
+
access_tags.each_pair do |tag_name, tag_data|
|
339
|
+
tag_data.each_pair do |tag_value, server_data|
|
340
|
+
puts " #{tag_name}".green+" => ".light_white+"#{tag_value}".send(server_data['sudo'] ? :red : :light_cyan)
|
341
|
+
end
|
342
|
+
end
|
343
|
+
puts
|
344
|
+
puts "Files".green
|
345
|
+
@user_data['files'].each_pair do |remote_path, file_data|
|
346
|
+
puts " ~/#{remote_path}".light_cyan+" (#{file_data['content_length']})".yellow
|
347
|
+
end
|
348
|
+
puts
|
349
|
+
return nil
|
350
|
+
end
|
351
|
+
|
352
|
+
# Access Tags
|
353
|
+
#
|
354
|
+
# Returns a list of the EC2 Tags user has associated to their profile
|
355
|
+
#
|
356
|
+
# @return [Hash] EC2 Tag names and their values to match on
|
357
|
+
def access_tags()
|
358
|
+
tag_columns = {}
|
359
|
+
@user_data.each_pair do |key, value|
|
360
|
+
key.match(/^TAG__(.*)/) do |match|
|
361
|
+
tag_columns[match[1]] = value
|
362
|
+
end
|
363
|
+
end
|
364
|
+
return tag_columns
|
365
|
+
end
|
366
|
+
|
367
|
+
private
|
368
|
+
|
369
|
+
def init_user()
|
370
|
+
@user_data['type'] = 'USER'
|
371
|
+
@user_data['shell'] = '/bin/bash'
|
372
|
+
@user_data['state'] = 'ACTIVE'
|
373
|
+
@user_data['files'] = {}
|
374
|
+
@user_data['identity'] = 0
|
375
|
+
@dirty = true
|
376
|
+
end
|
377
|
+
|
378
|
+
def get_user_data(attributes)
|
379
|
+
attributes.to_h.each_pair do |key, value|
|
380
|
+
marshaled = false
|
381
|
+
@marshaled_columns.each do |regex|
|
382
|
+
if(key.match(regex))
|
383
|
+
marshaled = true
|
384
|
+
break
|
385
|
+
end
|
386
|
+
end
|
387
|
+
if(marshaled)
|
388
|
+
value = JSON.load(value)
|
389
|
+
end
|
390
|
+
@user_data[key] = value
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
end
|
395
|
+
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
#Copyright 2013 Mark Trimmer
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
module ZepplenAWS
|
16
|
+
|
17
|
+
# = Manage Server Users
|
18
|
+
#
|
19
|
+
# This class is intended to be used by both the CLI scripts provided, and 3rd party tools
|
20
|
+
# written by you!
|
21
|
+
#
|
22
|
+
# The metadata for the environment (options set using the configure method), are stored
|
23
|
+
# in a DynamoDB row along with the users. The default name for this row is '__metadata__'.
|
24
|
+
#
|
25
|
+
#
|
26
|
+
class ServerUsers
|
27
|
+
|
28
|
+
# Server Users
|
29
|
+
#
|
30
|
+
# @param optional [String] Dynamo table name, if not provided will pull from Env, or default to 'users'
|
31
|
+
def initialize(dynamo_table = nil)
|
32
|
+
@dynamo_table = dynamo_table || Env[:dynamo_table] || 'users'
|
33
|
+
|
34
|
+
# I don't there there is any way to reach this error state....
|
35
|
+
if(@dynamo_table == nil)
|
36
|
+
raise Exceptions::Users::MissingOption, "DynamoDB Table Name Required"
|
37
|
+
end
|
38
|
+
|
39
|
+
@dynamo = AWS::DynamoDB.new()
|
40
|
+
@table = @dynamo.tables[@dynamo_table]
|
41
|
+
@table.hash_key = {:type => :string}
|
42
|
+
@table.range_key = {:user_name => :string}
|
43
|
+
if(!@table.exists?)
|
44
|
+
raise Exceptions::Users::NoDynamoTable, "Could Not Access DynamoDB Table: #{@dynamo_table}"
|
45
|
+
end
|
46
|
+
@local_user_data = {}
|
47
|
+
|
48
|
+
@metadata = @table.items['METADATA', '__metadata__']
|
49
|
+
end
|
50
|
+
|
51
|
+
# Exists?
|
52
|
+
#
|
53
|
+
# Reflects if there is a _metadata_ row availible. If not use needs to configure environment.
|
54
|
+
def exists?()
|
55
|
+
return @metadata.exists?
|
56
|
+
end
|
57
|
+
|
58
|
+
# Identity
|
59
|
+
#
|
60
|
+
# This number is incremented any time a configuration changes, or a user profile is updated.
|
61
|
+
# We use this to reduce the number of dynamo calls each client has to make.
|
62
|
+
#
|
63
|
+
# @return [Integer] Identity
|
64
|
+
def identity()
|
65
|
+
return @metadata.attributes[:identity].to_i
|
66
|
+
end
|
67
|
+
|
68
|
+
# User File Bucket
|
69
|
+
#
|
70
|
+
# @return [String] Name of S3 Bucket user files are stored in
|
71
|
+
def user_file_bucket()
|
72
|
+
return @metadata.attributes[:user_file_bucket]
|
73
|
+
end
|
74
|
+
|
75
|
+
# Set User File Bucket
|
76
|
+
#
|
77
|
+
# @param [String] Name of S3 Bucket to store user files in. Nil to disable feature
|
78
|
+
def user_file_bucket=(s3_path)
|
79
|
+
update_metadata(:user_file_bucket => s3_path)
|
80
|
+
return nil
|
81
|
+
end
|
82
|
+
|
83
|
+
# Max Key Age
|
84
|
+
#
|
85
|
+
# Number of Days to continue using an SSH Key before it is expired and removed from all servers
|
86
|
+
#
|
87
|
+
# @return [Integer] Max key age (days)
|
88
|
+
def max_key_age()
|
89
|
+
return @metadata.attributes[:max_key_age].to_i
|
90
|
+
end
|
91
|
+
|
92
|
+
# Set Max Key Age
|
93
|
+
#
|
94
|
+
# Number of Days to continue using an SSH Key before it is expired and removed from all servers
|
95
|
+
#
|
96
|
+
# @param [Integer] Max key age (days)
|
97
|
+
def max_key_age=(key_age)
|
98
|
+
update_metadata(:key_age => key_age)
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
|
102
|
+
# Next UID
|
103
|
+
#
|
104
|
+
# Next linux uid to use. We make sure that each user's uid is consistant accross all servers. This
|
105
|
+
# prevents users from having broken permissions when they are removed and re-added to an instance.
|
106
|
+
#
|
107
|
+
# @return [Integet] Next uid
|
108
|
+
def next_uid()
|
109
|
+
return @metadata.attributes[:next_uid].to_i
|
110
|
+
end
|
111
|
+
|
112
|
+
# Set Next UID
|
113
|
+
#
|
114
|
+
# Next linux uid to use. We make sure that each user's uid is consistant accross all servers. This
|
115
|
+
# prevents users from having broken permissions when they are removed and re-added to an instance.
|
116
|
+
#
|
117
|
+
# Warning: Be sure not to set to a range already used by existing users, or existing accounts on your servers.
|
118
|
+
#
|
119
|
+
# @param [Integer] Next uid
|
120
|
+
def next_uid=(next_uid)
|
121
|
+
update_metadata(:next_uid => next_uid)
|
122
|
+
return nil
|
123
|
+
end
|
124
|
+
|
125
|
+
# Sudo Group
|
126
|
+
#
|
127
|
+
# Group that user will be added to grant sudo access to an instance.
|
128
|
+
#
|
129
|
+
# @return [String] sudo group
|
130
|
+
def sudo_group()
|
131
|
+
return @metadata.attributes[:sudo_group]
|
132
|
+
end
|
133
|
+
|
134
|
+
# Set Sudo Group
|
135
|
+
#
|
136
|
+
# Group that user will be added to grant sudo access to an instance. Please be sure this group is configured
|
137
|
+
# correctly on all of your instances. Group should grant NOPASSWD access, as this script will NOT set a
|
138
|
+
# passwor for any user.
|
139
|
+
#
|
140
|
+
# @param [String] sudo group
|
141
|
+
def sudo_group=(sudo_group)
|
142
|
+
update_metadata(:sudo_group => sudo_group)
|
143
|
+
return nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# Tags
|
147
|
+
#
|
148
|
+
# Returns the list of EC2 tags accessible to taget users's access.
|
149
|
+
#
|
150
|
+
# @return [Array] List of EC2 tags valid for granting user access to servers.
|
151
|
+
def tags()
|
152
|
+
return @metadata.attributes[:tags].to_a
|
153
|
+
end
|
154
|
+
|
155
|
+
# Set Tags
|
156
|
+
#
|
157
|
+
# Overwrite existing tags with new Array of EC2 Tags
|
158
|
+
#
|
159
|
+
# @param [Array[String]] EC2 Tags that are valid for granting user access to servers.
|
160
|
+
def tags=(tags)
|
161
|
+
update_metadata(:tags => tags)
|
162
|
+
@metadata.attributes.update do |u|
|
163
|
+
u.set(:tags => tags)
|
164
|
+
u.add(:idenity => 1)
|
165
|
+
end
|
166
|
+
return nil
|
167
|
+
end
|
168
|
+
|
169
|
+
# Add Tags
|
170
|
+
#
|
171
|
+
# Add tags witn Array of EC2 Tags
|
172
|
+
#
|
173
|
+
# @param [Array[String]] EC2 Tags that are valid for granting user access to servers.
|
174
|
+
def add_tags(tags)
|
175
|
+
@metadata.attributes.update do |u|
|
176
|
+
u.add(:tags => tags)
|
177
|
+
u.add(:idenity => 1)
|
178
|
+
end
|
179
|
+
return nil
|
180
|
+
end
|
181
|
+
|
182
|
+
# Remove Tags
|
183
|
+
#
|
184
|
+
# Revmove tags witn Array of EC2 Tags
|
185
|
+
#
|
186
|
+
# @param [Array[String]] EC2 Tags that are no longer valid for granting user access to servers.
|
187
|
+
def remove_tags(tags)
|
188
|
+
@metadata.attributes.update do |u|
|
189
|
+
u.delete(:tags => tags)
|
190
|
+
u.add(:idenity => 1)
|
191
|
+
end
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
|
195
|
+
# Configure Envoronment
|
196
|
+
#
|
197
|
+
# Allows users to set multiple parameters at once
|
198
|
+
# == Valid Parameters
|
199
|
+
# :next_uid, :max_key_age, :tags, :sudo_group, :user_file_bucket
|
200
|
+
#
|
201
|
+
# @param [Hash] Parameter values to set
|
202
|
+
def configure(config)
|
203
|
+
valid_configs = [:next_uid, :max_key_age, :tags, :sudo_group]
|
204
|
+
to_use_config = config.select{|k,v| valid_configs.include?(k)}
|
205
|
+
@metadata.attributes.update do |item_data|
|
206
|
+
item_data.set(to_use_config)
|
207
|
+
if(config.has_key?(:user_file_bucket))
|
208
|
+
if(config[:user_file_bucket])
|
209
|
+
item_data.set(:user_file_bucket => config[:user_file_bucket])
|
210
|
+
else
|
211
|
+
item_data.delete(:user_file_bucket)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
if(@metadata.attributes[:identity] == nil)
|
215
|
+
item_data.set(:identity => 0)
|
216
|
+
else
|
217
|
+
item_data.add(:identity => 1)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
# Users
|
223
|
+
#
|
224
|
+
# Returns an array of ServerUser objects
|
225
|
+
#
|
226
|
+
# @return [Hash[ServerUser]]
|
227
|
+
def users()
|
228
|
+
users = {}
|
229
|
+
@table.items.where(:type => 'USER').each do |user_row|
|
230
|
+
users[user_row.attributes[:user_name]] = ServerUser.new(user_row.attributes[:user_name], @dynamo_table, user_row, @metadata, self)
|
231
|
+
end
|
232
|
+
return users
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
def update_metadata(data)
|
238
|
+
@metadata.attributes.update do |u|
|
239
|
+
data.each_pair do |key, value|
|
240
|
+
if(value)
|
241
|
+
u.set(key => value)
|
242
|
+
else
|
243
|
+
u.delete(key)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
u.add(:identity => 1)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def update_required?(local_users)
|
251
|
+
if(!File.readable?(local_users))
|
252
|
+
return true
|
253
|
+
end
|
254
|
+
@local_user_data = Yaml.load(local_users)
|
255
|
+
return false
|
256
|
+
end
|
257
|
+
|
258
|
+
end
|
259
|
+
end
|