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