Class: Forty::Sync

Inherits:
Object
  • Object
show all
Defined in:
lib/forty/sync.rb

Defined Under Namespace

Classes: Error

Instance Method Summary collapse

Constructor Details

#initialize(logger, master_username, production_schemas, acl_config, executor, dry_run = true) ⇒ Sync

Returns a new instance of Sync.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/forty/sync.rb', line 19

def initialize(logger, master_username, production_schemas, acl_config, executor, dry_run=true)
  @logger = logger or raise Error, 'No logger provided'
  @master_username = master_username or raise Error, 'No master username provided'
  @production_schemas = production_schemas or raise Error, 'No production schemas provided'
  @system_groups = ["pg_signal_backend"]
  @system_users = ["postgres"]
  @acl_config = acl_config or raise Error, 'No acl config provided'
  @acl_config['users'] ||= {}
  @acl_config['groups'] ||= {}

  @executor   = executor   or raise Error, 'No dwh executor provided'
  @dry_run = dry_run

  @logger.warn('Dry mode disabled, executing on production') unless @dry_run
end

Instance Method Details



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/forty/sync.rb', line 44

def banner
  @logger.info("Starting sync...\n____           __       \n   / __/___  _____/ /___  __\n  / /_/ __ \\\\/ ___/ __/ / / /\n / __/ /_/ / /  / /_/ /_/ /\n/_/  \\\\____/_/   \\\\__/\\\\__, /  Database ACL Sync\n               /____/   v0.2.0\n\n===============================================================================\n\nRunning in \#{@dry_run ? 'DRY-MODE (not enforcing state)' : 'PRODUCTION-MODE (enforcing state)'}\n\nConfiguration:\nMaster user:    \#{@master_username}\nSynced schemas: \#{@production_schemas.join(', ')}\nSystem users:   \#{@system_users.join(', ')}\nSystem groups:  \#{@system_groups.join(', ')}\n\n===============================================================================\n")
end

#runObject



35
36
37
38
39
40
41
42
# File 'lib/forty/sync.rb', line 35

def run
  banner()
  sync_users()
  sync_groups()
  sync_user_groups()
  sync_user_roles()
  sync_acl()
end

#sync_aclObject



178
179
180
181
182
# File 'lib/forty/sync.rb', line 178

def sync_acl
  sync_database_acl()
  sync_schema_acl()
  sync_table_acl()
end

#sync_database_aclObject



184
185
186
187
188
189
190
# File 'lib/forty/sync.rb', line 184

def sync_database_acl
  current_database_acl = _get_current_database_acl()
  defined_database_acl = _get_defined_database_acl()

  diverged = _sync_typed_acl('database', current_database_acl, defined_database_acl)
  @logger.info('All database privileges are in sync') if diverged == 0
end

#sync_groupsObject



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/forty/sync.rb', line 90

def sync_groups
  current_groups = _get_current_dwh_groups().keys
  defined_groups = @acl_config['groups'].keys

  undefined_groups = (current_groups - defined_groups - @system_groups).uniq.compact
  missing_groups = (defined_groups - current_groups).uniq.compact

  undefined_groups.each { |group| _delete_group(group) }
  missing_groups.each   { |group| _create_group(group) }

  @logger.info('All groups are in sync') if (undefined_groups.count + missing_groups.count) == 0
end

#sync_personal_schemasObject



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/forty/sync.rb', line 133

def sync_personal_schemas
  users = @acl_config['users'].keys
  users.each do |user|
    next if user.eql?(@master_username)
    schemas_owned_by_user = _get_currently_owned_schemas(user).uniq - @production_schemas
    unless schemas_owned_by_user.empty?
      tables_owned_by_user = _get_currently_owned_tables(user)
      schemas_owned_by_user.each do |schema|
        @executor.execute("set search_path=#{schema}")
        tables = @executor.execute("select tablename from pg_tables where schemaname='#{schema}'").map { |row| "#{schema}.#{row['tablename']}" }
        nonowned_tables_by_user = tables.uniq - tables_owned_by_user
        nonowned_tables_by_user.each { |table| _execute_statement("alter table #{table} owner to #{user};") }
      end
    end
  end
end

#sync_schema_aclObject



192
193
194
195
196
197
198
# File 'lib/forty/sync.rb', line 192

def sync_schema_acl
  current_schema_acl = _get_current_schema_acl()
  defined_schema_acl = _get_defined_schema_acl()

  diverged = _sync_typed_acl('schema', current_schema_acl, defined_schema_acl)
  @logger.info('All schema privileges are in sync') if diverged == 0
end

#sync_table_aclObject



200
201
202
203
204
205
206
# File 'lib/forty/sync.rb', line 200

def sync_table_acl
  current_table_acl = _get_current_table_acl()
  defined_table_acl = _get_defined_table_acl()

  diverged = _sync_typed_acl('table', current_table_acl, defined_table_acl)
  @logger.info('All table privileges are in sync') if diverged == 0
end

#sync_user_groupsObject



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/forty/sync.rb', line 103

def sync_user_groups
  current_user_groups = _get_current_user_groups()
  defined_user_groups = _get_defined_user_groups()
  _check_group_unknown(current_user_groups.keys, defined_user_groups.keys)

  current_users = _get_current_dwh_users().keys
  defined_users = _get_defined_users()
  _check_user_unknown(current_users, defined_users)

  diverged = 0

  current_user_groups.each do |group, list|
    current_list = list
    defined_list = defined_user_groups[group] || []

    undefined_assignments = (current_list - defined_list).uniq.compact
    missing_assignments = (defined_list - current_list).uniq.compact

    undefined_assignments.each { |user| _remove_user_from_group(user, group) }
    missing_assignments.each   { |user| _add_user_to_group(user, group) }

    current_group_diverged = (undefined_assignments.count + missing_assignments.count)
    diverged += current_group_diverged

    @logger.debug("Users of group #{group} are in sync") if current_group_diverged == 0
  end

  @logger.info('All user groups are in sync') if diverged == 0
end

#sync_user_rolesObject



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/forty/sync.rb', line 150

def sync_user_roles
  defined_user_roles = _get_defined_user_roles()
  current_user_roles = _get_current_user_roles()

  users = ((defined_user_roles.keys).concat(current_user_roles.keys)).uniq.compact

  diverged = 0

  users.each do |user|
    next if user.eql?(@master_username) or @system_users.include?(user)

    raise Error, "Users are not in sync #{user}" if current_user_roles[user].nil? or defined_user_roles[user].nil?

    undefined_roles = (current_user_roles[user] - defined_user_roles[user]).uniq.compact
    missing_roles   = (defined_user_roles[user] - current_user_roles[user]).uniq.compact

    current_roles_diverged = (undefined_roles.count + missing_roles.count)
    diverged += current_roles_diverged

    undefined_roles.each { |role| _execute_statement("alter user #{user} no#{role};") }
    missing_roles.each   { |role| _execute_statement("alter user #{user} #{role};") }

    @logger.debug("Roles of #{user} are in sync") if current_roles_diverged == 0
  end

  @logger.info('All user roles are in sync') if diverged == 0
end

#sync_usersObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/forty/sync.rb', line 68

def sync_users
  current_users = _get_current_dwh_users.keys
  defined_users = @acl_config['users'].keys

  undefined_users = (current_users - defined_users - @system_users).uniq.compact
  missing_users = (defined_users - current_users).uniq.compact

  @logger.debug("Undefined users: #{undefined_users}")
  @logger.debug("Missing users: #{missing_users}")
  undefined_users.each { |user| _delete_user(user) }

  missing_users.each do |user|
    roles = @acl_config['users'][user]['roles'] || []
    password = @acl_config['users'][user]['password']
    search_path = @production_schemas.join(',')

    _create_user(user, password, roles, search_path)
  end

  @logger.info('All users are in sync') if (undefined_users.count + missing_users.count) == 0
end