-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Expand file tree
/
Copy pathmigrate-dynamicroles.py
More file actions
executable file
·145 lines (121 loc) · 6.09 KB
/
migrate-dynamicroles.py
File metadata and controls
executable file
·145 lines (121 loc) · 6.09 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import os
import sys
import uuid
from contextlib import closing
from optparse import OptionParser
try:
import mysql.connector
except ImportError:
print("mysql.connector cannot be imported, please install mysql-connector-python")
sys.exit(1)
dryrun = False
def runSql(conn, query):
if dryrun:
print("Running SQL query: " + query)
return
with closing(conn.cursor()) as cursor:
cursor.execute(query)
def migrateApiRolePermissions(apis, conn):
# All allow for root admin role Admin(id:1)
runSql(conn, "INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), 1, '*', 'ALLOW', 0);")
# Migrate rules based on commands.properties rule for ResourceAdmin(id:2), DomainAdmin(id:3), User(id:4)
octetKey = {2:2, 3:4, 4:8}
for role in [2, 3, 4]:
sortOrder = 0
for api in sorted(apis.keys()):
# Ignore auth commands
if api in ['login', 'logout', 'samlSso', 'samlSlo', 'listIdps', 'listAndSwitchSamlAccount', 'getSPMetadata']:
continue
if (octetKey[role] & int(apis[api])) > 0:
runSql(conn, "INSERT INTO `cloud`.`role_permissions` (`uuid`, `role_id`, `rule`, `permission`, `sort_order`) values (UUID(), %d, '%s', 'ALLOW', %d);" % (role, api, sortOrder))
sortOrder += 1
print("Static role permissions from commands.properties have been migrated into the db")
def enableDynamicApiChecker(conn):
runSql(conn, "UPDATE `cloud`.`configuration` SET value='true' where name='dynamic.apichecker.enabled'")
conn.commit()
conn.close()
print("Dynamic role based API checker has been enabled!")
def main():
parser = OptionParser()
parser.add_option("-b", "--db", action="store", type="string", dest="db", default="cloud",
help="The name of the database, default: cloud")
parser.add_option("-u", "--user", action="store", type="string", dest="user", default="cloud",
help="User name a MySQL user with privileges on cloud database")
parser.add_option("-p", "--password", action="store", type="string", dest="password", default="cloud",
help="Password of a MySQL user with privileges on cloud database")
parser.add_option("-H", "--host", action="store", type="string", dest="host", default="127.0.0.1",
help="Host or IP of the MySQL server")
parser.add_option("-P", "--port", action="store", type="int", dest="port", default=3306,
help="Host or IP of the MySQL server")
parser.add_option("-f", "--properties-file", action="store", type="string", dest="commandsfile", default="/etc/cloudstack/management/commands.properties",
help="The commands.properties file")
parser.add_option("-D", "--default", action="store_true", dest="defaultRules", default=False,
help="")
parser.add_option("-d", "--dryrun", action="store_true", dest="dryrun", default=False,
help="Dry run and debug operations this tool will perform")
(options, args) = parser.parse_args()
print("Apache CloudStack Role Permission Migration Tool")
print("(c) Apache CloudStack Authors and the ASF, under the Apache License, Version 2.0\n")
global dryrun
if options.dryrun:
dryrun = True
conn = mysql.connector.connect(
host=options.host,
user=options.user,
passwd=options.password,
port=int(options.port),
db=options.db)
if options.defaultRules:
print("Applying the default role permissions, ignoring any provided properties files(s).")
enableDynamicApiChecker(conn)
sys.exit(0)
if not os.path.isfile(options.commandsfile):
print("Provided commands.properties cannot be accessed or does not exist.")
print("Please check passed options, or run only with --default option to use the default role permissions.")
sys.exit(1)
while True:
choice = input("Running this migration tool will remove any " +
"default-role permissions from cloud.role_permissions. " +
"Do you want to continue? [y/N]").lower()
if choice == 'y':
break
else:
print("Aborting!")
sys.exit(1)
# Generate API to permission octet map
apiMap = {}
with open(options.commandsfile) as f:
for line in f.readlines():
if not line or line == '' or line == '\n' or line == '\r\n' or line.startswith('#'):
continue
name, value = line.split('=')
apiMap[name.strip()] = value.strip().split(';')[-1]
# Rename and deprecate old commands.properties file
if not dryrun:
os.rename(options.commandsfile, options.commandsfile + '.deprecated')
print("The commands.properties file has been deprecated and moved at: " + options.commandsfile + '.deprecated')
# Truncate any rules in cloud.role_permissions table
runSql(conn, "DELETE FROM `cloud`.`role_permissions` WHERE `role_id` in (1,2,3,4);")
# Migrate rules from commands.properties to cloud.role_permissions
migrateApiRolePermissions(apiMap, conn)
enableDynamicApiChecker(conn)
if __name__ == '__main__':
main()