-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path1_2_0_upgrade_2_0_0.py
275 lines (230 loc) · 8.66 KB
/
1_2_0_upgrade_2_0_0.py
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
146
147
148
149
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
from __future__ import print_function
import os
import sys
import dataset
from sqlalchemy_utils import drop_database
from CTFd import config, create_app
from CTFd.utils import string_types
# This is important to allow access to the CTFd application factory
sys.path.append(os.getcwd())
def cast_bool(value):
if value and value.isdigit():
return int(value)
elif value and isinstance(value, string_types):
if value.lower() == "true":
return True
elif value.lower() == "false":
return False
else:
return value
if __name__ == "__main__":
print("/*\\ Migrating your database to 2.0.0 can potentially lose data./*\\")
print(
"""/*\\ Please be sure to back up all data by:
* creating a CTFd export
* creating a dump of your actual database
* and backing up the CTFd source code directory"""
)
print("/*\\ CTFd maintainers are not responsible for any data loss! /*\\")
if input("Run database migrations (Y/N)").lower().strip() == "y":
pass
else:
print("/*\\ Aborting database migrations... /*\\")
print("/*\\ Exiting... /*\\")
exit(1)
db_url = config.Config.SQLALCHEMY_DATABASE_URI
old_data = {}
old_conn = dataset.connect(config.Config.SQLALCHEMY_DATABASE_URI)
tables = old_conn.tables
for table in tables:
old_data[table] = old_conn[table].all()
if "alembic_version" in old_data:
old_data.pop("alembic_version")
print("Current Tables:")
for table in old_data.keys():
print("\t", table)
old_conn.executable.close()
print("DROPPING DATABASE")
drop_database(db_url)
app = create_app()
new_conn = dataset.connect(config.Config.SQLALCHEMY_DATABASE_URI)
print("MIGRATING Challenges")
for challenge in old_data["challenges"]:
hidden = challenge.pop("hidden")
challenge["state"] = "hidden" if hidden else "visible"
new_conn["challenges"].insert(dict(challenge))
del old_data["challenges"]
print("MIGRATING Teams")
for team in old_data["teams"]:
admin = team.pop("admin")
team["type"] = "admin" if admin else "user"
team["hidden"] = bool(team.pop("banned"))
team["banned"] = False
team["verified"] = bool(team.pop("verified"))
new_conn["users"].insert(dict(team))
del old_data["teams"]
print("MIGRATING Pages")
for page in old_data["pages"]:
page["content"] = page.pop("html")
new_conn["pages"].insert(dict(page))
del old_data["pages"]
print("MIGRATING Keys")
for key in old_data["keys"]:
key["challenge_id"] = key.pop("chal")
key["content"] = key.pop("flag")
new_conn["flags"].insert(dict(key))
del old_data["keys"]
print("MIGRATING Tags")
for tag in old_data["tags"]:
tag["challenge_id"] = tag.pop("chal")
tag["value"] = tag.pop("tag")
new_conn["tags"].insert(dict(tag))
del old_data["tags"]
print("MIGRATING Files")
for f in old_data["files"]:
challenge_id = f.pop("chal")
if challenge_id:
f["challenge_id"] = challenge_id
f["type"] = "challenge"
else:
f["page_id"] = None
f["type"] = "page"
new_conn["files"].insert(dict(f))
del old_data["files"]
print("MIGRATING Hints")
for hint in old_data["hints"]:
hint["type"] = "standard"
hint["challenge_id"] = hint.pop("chal")
hint["content"] = hint.pop("hint")
new_conn["hints"].insert(dict(hint))
del old_data["hints"]
print("MIGRATING Unlocks")
for unlock in old_data["unlocks"]:
unlock["user_id"] = unlock.pop(
"teamid"
) # This is intentional as previous CTFds are effectively in user mode
unlock["target"] = unlock.pop("itemid")
unlock["type"] = unlock.pop("model")
new_conn["unlocks"].insert(dict(unlock))
del old_data["unlocks"]
print("MIGRATING Awards")
for award in old_data["awards"]:
award["user_id"] = award.pop(
"teamid"
) # This is intentional as previous CTFds are effectively in user mode
new_conn["awards"].insert(dict(award))
del old_data["awards"]
submissions = []
for solve in old_data["solves"]:
solve.pop("id") # ID of a solve doesn't really matter
solve["challenge_id"] = solve.pop("chalid")
solve["user_id"] = solve.pop("teamid")
solve["provided"] = solve.pop("flag")
solve["type"] = "correct"
solve["model"] = "solve"
submissions.append(solve)
for wrong_key in old_data["wrong_keys"]:
wrong_key.pop("id") # ID of a fail doesn't really matter.
wrong_key["challenge_id"] = wrong_key.pop("chalid")
wrong_key["user_id"] = wrong_key.pop("teamid")
wrong_key["provided"] = wrong_key.pop("flag")
wrong_key["type"] = "incorrect"
wrong_key["model"] = "wrong_key"
submissions.append(wrong_key)
submissions = sorted(submissions, key=lambda k: k["date"])
print("MIGRATING Solves & WrongKeys")
for submission in submissions:
model = submission.pop("model")
if model == "solve":
new_id = new_conn["submissions"].insert(dict(submission))
submission["id"] = new_id
new_conn["solves"].insert(dict(submission))
elif model == "wrong_key":
new_conn["submissions"].insert(dict(submission))
del old_data["solves"]
del old_data["wrong_keys"]
print("MIGRATING Tracking")
for tracking in old_data["tracking"]:
tracking["user_id"] = tracking.pop("team")
new_conn["tracking"].insert(dict(tracking))
del old_data["tracking"]
print("MIGRATING Config")
banned = ["ctf_version"]
workshop_mode = None
hide_scores = None
prevent_registration = None
view_challenges_unregistered = None
view_scoreboard_if_authed = None
challenge_visibility = "private"
registration_visibility = "public"
score_visibility = "public"
account_visibility = "public"
for c in old_data["config"]:
c.pop("id")
if c["key"] == "workshop_mode":
workshop_mode = cast_bool(c["value"])
elif c["key"] == "hide_scores":
hide_scores = cast_bool(c["value"])
elif c["key"] == "prevent_registration":
prevent_registration = cast_bool(c["value"])
elif c["key"] == "view_challenges_unregistered":
view_challenges_unregistered = cast_bool(c["value"])
elif c["key"] == "view_scoreboard_if_authed":
view_scoreboard_if_authed = cast_bool(c["value"])
if c["key"] not in banned:
new_conn["config"].insert(dict(c))
if workshop_mode:
score_visibility = "admins"
account_visibility = "admins"
if hide_scores:
score_visibility = "hidden"
if prevent_registration:
registration_visibility = "private"
if view_challenges_unregistered:
challenge_visibility = "public"
if view_scoreboard_if_authed:
score_visibility = "private"
new_conn["config"].insert({"key": "user_mode", "value": "users"})
new_conn["config"].insert(
{"key": "challenge_visibility", "value": challenge_visibility}
)
new_conn["config"].insert(
{"key": "registration_visibility", "value": registration_visibility}
)
new_conn["config"].insert({"key": "score_visibility", "value": score_visibility})
new_conn["config"].insert(
{"key": "account_visibility", "value": account_visibility}
)
del old_data["config"]
manual = []
not_created = []
print("MIGRATING extra tables")
for table in old_data.keys():
print("MIGRATING", table)
new_conn.create_table(table, primary_id=False)
data = old_data[table]
ran = False
for row in data:
new_conn[table].insert(dict(row))
ran = True
else: # We finished inserting
if ran:
manual.append(table)
if ran is False:
not_created.append(table)
print("Migration completed.")
print(
"The following tables require manual setting of primary keys and manual inspection"
)
for table in manual:
print("\t", table)
print(
"For example you can use the following commands if you know that the PRIMARY KEY for the table is `id`:"
)
for table in manual:
print("\t", "ALTER TABLE `{table}` ADD PRIMARY KEY(id)".format(table=table))
print(
"The following tables were not created because they were empty and must be manually recreated (e.g. app.db.create_all()"
)
for table in not_created:
print("\t", table)