blob: 3f0176cb6542e0feda2ebfca0e38ee277035e90d (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
# frozen_string_literal: true
module Gitlab
module Database
module QueryAnalyzers
class RestrictAllowedSchemas < Base
UnsupportedSchemaError = Class.new(QueryAnalyzerError)
DDLNotAllowedError = Class.new(UnsupportedSchemaError)
DMLNotAllowedError = Class.new(UnsupportedSchemaError)
DMLAccessDeniedError = Class.new(UnsupportedSchemaError)
IGNORED_SCHEMAS = %i[gitlab_shared].freeze
class << self
def enabled?
true
end
def allowed_gitlab_schemas
self.context[:allowed_gitlab_schemas]
end
def allowed_gitlab_schemas=(value)
self.context[:allowed_gitlab_schemas] = value
end
def analyze(parsed)
# If list of schemas is empty, we allow only DDL changes
if self.dml_mode?
self.restrict_to_dml_only(parsed)
else
self.restrict_to_ddl_only(parsed)
end
end
def require_ddl_mode!(message = "")
return unless self.context
self.raise_dml_not_allowed_error(message) if self.dml_mode?
end
def require_dml_mode!(message = "")
return unless self.context
self.raise_ddl_not_allowed_error(message) if self.ddl_mode?
end
private
def restrict_to_ddl_only(parsed)
tables = self.dml_tables(parsed)
schemas = self.dml_schemas(tables)
if schemas.any?
self.raise_dml_not_allowed_error("Modifying of '#{tables}' (#{schemas.to_a}) with '#{parsed.sql}'")
end
end
def restrict_to_dml_only(parsed)
if parsed.pg.ddl_tables.any?
self.raise_ddl_not_allowed_error("Modifying of '#{parsed.pg.ddl_tables}' with '#{parsed.sql}'")
end
if parsed.pg.ddl_functions.any?
self.raise_ddl_not_allowed_error("Modifying of '#{parsed.pg.ddl_functions}' with '#{parsed.sql}'")
end
tables = self.dml_tables(parsed)
schemas = self.dml_schemas(tables)
if (schemas - self.allowed_gitlab_schemas).any?
raise DMLAccessDeniedError, \
"Select/DML queries (SELECT/UPDATE/DELETE) do access '#{tables}' (#{schemas.to_a}) " \
"which is outside of list of allowed schemas: '#{self.allowed_gitlab_schemas}'. " \
"#{documentation_url}"
end
end
def dml_mode?
self.allowed_gitlab_schemas&.any?
end
def ddl_mode?
!self.dml_mode?
end
def dml_tables(parsed)
parsed.pg.select_tables + parsed.pg.dml_tables
end
def dml_schemas(tables)
extra_schemas = ::Gitlab::Database::GitlabSchema.table_schemas(tables)
extra_schemas.subtract(IGNORED_SCHEMAS)
extra_schemas
end
def raise_dml_not_allowed_error(message)
raise DMLNotAllowedError, \
"Select/DML queries (SELECT/UPDATE/DELETE) are disallowed in the DDL (structure) mode. " \
"#{message}. #{documentation_url}" \
end
def raise_ddl_not_allowed_error(message)
raise DDLNotAllowedError, \
"DDL queries (structure) are disallowed in the Select/DML (SELECT/UPDATE/DELETE) mode. " \
"#{message}. #{documentation_url}"
end
def documentation_url
"For more information visit: https://docs.gitlab.com/ee/development/database/migrations_for_multiple_databases.html"
end
end
end
end
end
end
|