By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 gitHTTP.py

View raw Download
text/x-script.python • 11.56 kiB
Python script, ASCII text executable
        
            
1
import uuid
2
3
from gitme import app, User, Repo, db, bcrypt
4
import os
5
import shutil
6
import config
7
import flask
8
import git
9
import subprocess
10
from flask_httpauth import HTTPBasicAuth
11
12
auth = HTTPBasicAuth()
13
14
15
@auth.verify_password
16
def verifyPassword(username, password):
17
user = User.query.filter_by(username=username).first()
18
19
if user and bcrypt.check_password_hash(user.passwordHashed, password):
20
flask.session["username"] = user.username
21
flask.g.user = username
22
return True
23
24
return False
25
26
27
@app.route("/git/<username>/<repository>/<path:subpath>", methods=["PROPFIND"])
28
def gitList(username, repository, subpath):
29
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository, ".git", subpath)
30
31
if not os.path.exists(serverRepoLocation):
32
flask.abort(404)
33
34
text = """<?xml version="1.0" encoding="utf-8" ?>
35
<D:multistatus xmlns:D="DAV:">
36
"""
37
38
for file in os.listdir(serverRepoLocation):
39
text += f"""
40
<D:response>
41
<D:href>/git/{username}/{repository}/</D:href>
42
"""
43
44
if os.path.isdir(file):
45
text += """
46
<D:propstat>
47
<D:prop>
48
<D:resourcetype>
49
<D:collection/>
50
</D:resourcetype>
51
</D:prop>
52
<D:status>HTTP/1.1 200 OK</D:status>
53
</D:propstat>
54
"""
55
else:
56
text += """
57
<D:propstat>
58
<D:prop/>
59
<D:status>HTTP/1.1 200 OK</D:status>
60
</D:propstat>
61
"""
62
63
text += """
64
</D:response>"""
65
66
text += """
67
</D:multistatus>"""
68
69
return text
70
71
72
@app.route("/git/<username>/<repository>/", methods=["GET", "POST"])
73
def gitRoot(username, repository):
74
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
75
app.logger.info(f"Loading {serverRepoLocation}")
76
77
if not os.path.exists(serverRepoLocation):
78
app.logger.error(f"Cannot load {serverRepoLocation}")
79
flask.abort(404)
80
81
repo = git.Repo(serverRepoLocation)
82
83
84
@app.route("/git/<username>/<repository>/<path:git_path>", methods=["GET", "POST"])
85
@auth.login_required
86
def gitInfo(username, repository, git_path=""):
87
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
88
app.logger.info(f"Loading {serverRepoLocation}")
89
90
if not os.path.exists(serverRepoLocation):
91
app.logger.error(f"Cannot load {serverRepoLocation}")
92
flask.abort(404)
93
94
repo = git.Repo(serverRepoLocation)
95
96
if flask.request.method == "GET":
97
method = "upload-pack"
98
result = repo.git.upload_pack()
99
elif flask.request.method == "POST":
100
method = "receive-pack"
101
if flask.request.headers.get("Content-Type") == "application/x-git-receive-pack-request":
102
result = repo.git.receive_pack()
103
else:
104
result = "Not supported"
105
106
response = flask.Response(result, content_type=f"application/x-git-{method}-result")
107
return response
108
109
110
@app.route("/git/<username>/<repository>/<path:git_path>", methods=["MKCOL"])
111
def gitDummyMkCol(username, repository, git_path):
112
return "", 200
113
114
115
@app.route("/git/<username>/<repository>/info/", methods=["MKCOL"])
116
def gitMakeInfo(username, repository):
117
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
118
app.logger.info(f"Loading {serverRepoLocation}")
119
120
if not os.path.exists(serverRepoLocation):
121
app.logger.error(f"Cannot load {serverRepoLocation}")
122
flask.abort(404)
123
124
if not os.path.exists(os.path.join(serverRepoLocation, ".git", "info")):
125
os.makedirs(os.path.join(serverRepoLocation, ".git", "info"))
126
127
return "", 200
128
129
130
@app.route("/git/<username>/<repository>/info/refs")
131
def gitInfoRefs(username, repository):
132
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
133
app.logger.info(f"Loading {serverRepoLocation}")
134
repoData = Repo.query.filter_by(route=f"/{username}/{repository}").first()
135
136
if not os.path.exists(serverRepoLocation):
137
app.logger.error(f"Cannot load {serverRepoLocation}")
138
flask.abort(404)
139
140
text = ""
141
repo = git.Repo(serverRepoLocation)
142
for i in repo.heads:
143
if i.name == repoData.defaultBranch:
144
text = f"{i.commit.hexsha}\tHEAD\n"
145
break
146
147
for ref in repo.heads:
148
text += f"{ref.commit.hexsha}\trefs/heads/{ref.name}\n"
149
150
app.logger.warning(text)
151
152
return flask.Response(text, content_type=f"application/x-{flask.request.args.get('service')}-result")
153
154
155
@app.route("/git/<username>/<repository>/objects/info/alternates", methods=["GET"])
156
@auth.login_required
157
def gitInfoAlternates(username, repository):
158
text = "#Alternate object stores\n"
159
return flask.Response(text, content_type="text/plain")
160
161
162
@app.route("/git/<username>/<repository>/objects/info/http-alternates")
163
@auth.login_required
164
def gitHttpAlternates(username, repository):
165
return flask.Response("", content_type="text/plain")
166
167
168
@app.route("/git/<username>/<repository>/HEAD")
169
@auth.login_required
170
def gitHead(username, repository):
171
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
172
app.logger.info(f"Loading {serverRepoLocation}")
173
174
if not os.path.exists(serverRepoLocation):
175
app.logger.error(f"Cannot load {serverRepoLocation}")
176
flask.abort(404)
177
178
repo = git.Repo(serverRepoLocation)
179
text = f"ref: {repo.head.ref}\n"
180
return flask.Response(text, content_type="text/plain")
181
182
183
@app.route("/git/<username>/<repository>/objects/info/packs")
184
def gitInfoPacks(username, repository):
185
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository, ".git")
186
packs = [f for f in os.listdir(os.path.join(serverRepoLocation, "objects", "pack")) if f.endswith(".pack")]
187
text = "\n".join(packs) + "\n"
188
return flask.Response(text, content_type="text/plain")
189
190
191
@app.route("/git/<username>/<repository>/objects/<path:objectPath>")
192
@auth.login_required
193
def gitObject(username, repository, objectPath):
194
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
195
objectFullPath = os.path.join(serverRepoLocation, ".git/objects", objectPath)
196
197
if not os.path.exists(objectFullPath):
198
return flask.Response("Object not found", status=404, content_type="text/plain")
199
200
with open(objectFullPath, "rb") as f:
201
objectData = f.read()
202
203
return flask.Response(objectData, content_type="application/octet-stream")
204
205
206
@app.route("/git/<username>/<repository>/<path:itemPath>", methods=["PUT"])
207
# @auth.login_required
208
def gitPut(username, repository, itemPath):
209
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
210
itemFullPath = os.path.join(serverRepoLocation, ".git", itemPath)
211
212
if not os.path.exists(os.path.dirname(itemFullPath)):
213
os.makedirs(os.path.dirname(itemFullPath))
214
215
with open(itemFullPath, "wb") as f:
216
f.write(flask.request.data)
217
218
return "", 200
219
220
221
@app.route("/git/<username>/<repository>/<path:itemPath>", methods=["MOVE"])
222
# @auth.login_required
223
def gitMove(username, repository, itemPath):
224
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
225
itemFullPath = os.path.join(serverRepoLocation, ".git", itemPath)
226
newPath = os.path.join(serverRepoLocation, ".git", flask.request.headers["destination"].split("/", 6)[-1])
227
228
app.logger.info(f"move {itemPath} to {newPath}")
229
230
shutil.move(itemFullPath, newPath)
231
return "", 200
232
233
234
@app.route("/git/<username>/<repository>/git-upload-pack", methods=["POST"])
235
@auth.login_required
236
def gitUploadPack(username, repository):
237
serverRepoLocation = os.path.join(config.REPOS_PATH, os.path.join(username, repository))
238
cmd = ["git", "--git-dir", serverRepoLocation, "upload-pack"]
239
240
gitProcess = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
241
242
inputData = flask.request.get_data()
243
output = gitProcess.communicate(input=inputData)
244
return flask.Response(output[0], content_type="application/x-git-upload-pack-result")
245
246
247
@app.route("/git/<username>/<repository>/git-receive-pack", methods=["POST"])
248
@auth.login_required
249
def gitReceivePack(username, repository):
250
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
251
252
proc = subprocess.Popen(['git', '--work-tree', serverRepoLocation, 'receive-pack'], stdin=subprocess.PIPE,
253
stdout=subprocess.PIPE)
254
proc.stdin.write(flask.request.get_data())
255
proc.stdin.close()
256
response = proc.stdout.read()
257
258
return flask.Response(response, content_type='application/x-git-receive-pack-result')
259
260
261
@app.route("/git/<username>/<repository>/", methods=["PROPFIND"])
262
def gitPropFind(username, repository):
263
serverRepoLocation = os.path.join(config.REPOS_PATH, username, repository)
264
265
if not os.path.exists(serverRepoLocation):
266
flask.abort(404)
267
268
repo = git.Repo(serverRepoLocation)
269
270
branches = ""
271
for branch in repo.heads:
272
branches += f"""
273
<ns0:branch xmlns="DAV:">
274
<ns0:name>{branch.name}</ns0:name>
275
</ns0:branch>
276
"""
277
278
response = f"""<?xml version="1.0" encoding="UTF-8"?>
279
<D:multistatus xmlns:D="DAV:">
280
<D:response>
281
<D:href>/git/{username}/{repository}/</D:href>
282
<D:propstat>
283
<D:status>HTTP/1.1 200 OK</D:status>
284
<D:prop>
285
<D:displayname>{repository}</D:displayname>
286
<D:resourcetype><D:collection/></D:resourcetype>
287
<D:supportedlock>
288
<D:lockentry>
289
<D:lockscope><D:exclusive/></D:lockscope>
290
<D:locktype><D:write/></D:locktype>
291
</D:lockentry>
292
</D:supportedlock>
293
</D:prop>
294
</D:propstat>
295
</D:response>
296
</D:multistatus>
297
"""
298
299
return flask.Response(response, content_type="application/xml")
300
301
302
locks = {}
303
304
305
@app.route("/git/<username>/<repository>/<path:subpath>", methods=["LOCK", "UNLOCK"])
306
def gitLock(username, repository, subpath):
307
if flask.request.method == "LOCK":
308
scope = flask.request.args.get("lockscope")
309
type_ = flask.request.args.get("locktype")
310
311
if username + "/" + repository in locks:
312
return "Lock already exists", 423
313
314
lockToken = uuid.uuid4().hex
315
if config.locking:
316
locks[username + "/" + repository] = {
317
"scope": scope,
318
"type": type_,
319
"token": lockToken
320
}
321
322
text = f"""<?xml version="1.0" encoding="UTF-8"?>
323
<D:prop>
324
<D:lockdiscovery>
325
<D:activelock>
326
<D:lockscope><D:exclusive/></D:lockscope>
327
<D:locktype><D:write/></D:locktype>
328
<D:depth>Infinity</D:depth>
329
<D:owner>
330
<D:href>https://{config.BASE_DOMAIN}/{username}</D:href>
331
</D:owner>
332
<D:timeout>Second-600</D:timeout>
333
<D:locktoken>
334
<D:href>opaquelocktoken:{lockToken}</D:href>
335
</D:locktoken>
336
</D:activelock>
337
</D:lockdiscovery>
338
</D:prop>
339
"""
340
341
return text
342
elif flask.request.method == "UNLOCK":
343
lockToken = flask.request.headers.get("Lock-Token")
344
345
if config.locking and locks[username + "/" + repository]["token"] == lockToken:
346
del locks[username + "/" + repository]
347
348
return ""
349