Skip to content

Commit 19d7a5b

Browse files
committed
Add authentification check
1 parent 5767128 commit 19d7a5b

File tree

5 files changed

+150
-95
lines changed

5 files changed

+150
-95
lines changed

src/HttpRequest.cpp

+79-60
Original file line numberDiff line numberDiff line change
@@ -6,80 +6,99 @@
66
static class Curl
77
{
88
public:
9-
Curl()
10-
{
11-
curl_global_init(CURL_GLOBAL_ALL);
12-
}
13-
~Curl()
14-
{
15-
curl_global_cleanup();
16-
}
9+
Curl()
10+
{
11+
curl_global_init(CURL_GLOBAL_ALL);
12+
}
13+
~Curl()
14+
{
15+
curl_global_cleanup();
16+
}
1717

1818
} curl;
1919

2020
HttpRequest::HttpRequest() : curl(0) {}
2121

2222
HttpRequest::~HttpRequest()
2323
{
24-
if(curl)
25-
curl_easy_cleanup(curl);
24+
if (curl)
25+
curl_easy_cleanup(curl);
2626
}
2727

28-
bool HttpRequest::get(const String& url, Buffer& data, bool checkCertificate)
28+
bool HttpRequest::get(const String &url, Buffer &data, const HashMap<String, String> &headerFields, bool checkCertificate)
2929
{
30-
if(!curl)
31-
{
32-
curl = curl_easy_init();
33-
if(!curl)
30+
if (!curl)
31+
{
32+
curl = curl_easy_init();
33+
if (!curl)
34+
{
35+
error = "Could not initialize curl.";
36+
return false;
37+
}
38+
}
39+
else
40+
curl_easy_reset(curl);
41+
42+
struct WriteResult
43+
{
44+
static size_t writeResponse(void *ptr, size_t size, size_t nmemb, void *stream)
45+
{
46+
WriteResult *result = (struct WriteResult *)stream;
47+
size_t newBytes = size * nmemb;
48+
size_t totalSize = result->buffer->size() + newBytes;
49+
if (totalSize > (size_t)result->buffer->capacity())
50+
result->buffer->reserve(totalSize * 2);
51+
result->buffer->append((const byte *)ptr, newBytes);
52+
return newBytes;
53+
}
54+
55+
Buffer *buffer;
56+
} writeResult = {&data};
57+
58+
curl_easy_setopt(curl, CURLOPT_URL, (const char *)url);
59+
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteResult::writeResponse);
60+
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writeResult);
61+
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 40);
62+
if (!checkCertificate)
63+
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
64+
65+
class SList
66+
{
67+
public:
68+
SList() : _slist(nullptr) {}
69+
~SList() { if (_slist) curl_slist_free_all(_slist);}
70+
operator curl_slist*() {return _slist;}
71+
void append(const char* value) {_slist = curl_slist_append(_slist, value);}
72+
private:
73+
curl_slist* _slist;
74+
};
75+
76+
SList header;
77+
if (!headerFields.isEmpty())
78+
{
79+
for (HashMap<String, String>::Iterator i = headerFields.begin(), end = headerFields.end(); i != end; ++i)
80+
header.append(i.key() + ": " + *i);
81+
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, (curl_slist*)header);
82+
}
83+
84+
data.clear();
85+
data.reserve(1500);
86+
87+
CURLcode status = curl_easy_perform(curl);
88+
if (status != 0)
3489
{
35-
error = "Could not initialize curl.";
36-
return false;
90+
error.printf("%s.", curl_easy_strerror(status));
91+
return false;
3792
}
38-
}
39-
else
40-
curl_easy_reset(curl);
4193

42-
struct WriteResult
43-
{
44-
static size_t writeResponse(void *ptr, size_t size, size_t nmemb, void *stream)
94+
long code;
95+
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
96+
if (code != 200)
4597
{
46-
WriteResult* result = (struct WriteResult *)stream;
47-
size_t newBytes = size * nmemb;
48-
size_t totalSize = result->buffer->size() + newBytes;
49-
if(totalSize > (size_t)result->buffer->capacity())
50-
result->buffer->reserve(totalSize * 2);
51-
result->buffer->append((const byte*)ptr, newBytes);
52-
return newBytes;
98+
error.printf("Server responded with code %u.", (uint)code);
99+
return false;
53100
}
54101

55-
Buffer* buffer;
56-
} writeResult = { &data };
57-
58-
curl_easy_setopt(curl, CURLOPT_URL, (const char*)url);
59-
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteResult::writeResponse);
60-
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &writeResult);
61-
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 40);
62-
if(!checkCertificate)
63-
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
64-
65-
data.clear();
66-
data.reserve(1500);
67-
68-
CURLcode status = curl_easy_perform(curl);
69-
if(status != 0)
70-
{
71-
error.printf("%s.", curl_easy_strerror(status));
72-
return false;
73-
}
74-
75-
long code;
76-
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
77-
if(code != 200)
78-
{
79-
error.printf("Server responded with code %u.", (uint)code);
80-
return false;
81-
}
82-
83-
error.clear();
84-
return true;
102+
error.clear();
103+
return true;
85104
}

src/HttpRequest.hpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <nstd/String.hpp>
66
#include <nstd/Buffer.hpp>
7+
#include <nstd/HashMap.hpp>
78

89
class HttpRequest
910
{
@@ -13,7 +14,7 @@ class HttpRequest
1314

1415
const String& getErrorString() {return error;}
1516

16-
bool get(const String& url, Buffer& data, bool checkCertificate = true);
17+
bool get(const String& url, Buffer& data, const HashMap<String, String>& headerFields = HashMap<String, String>(), bool checkCertificate = true);
1718

1819
private:
1920
void* curl;

src/Settings.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
#include <nstd/File.hpp>
55
#include <nstd/List.hpp>
66
#include <nstd/Log.hpp>
7+
#include <nstd/Directory.hpp>
78

89
Settings::Settings()
910
: listenAddr{Socket::anyAddress, 80}
10-
, cacheDir("/tmp/gchsd")
11+
, cacheDir(Directory::getTempDirectory() + "/gchsd")
1112
{
1213
;
1314
}

src/Worker.cpp

+66-32
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include <nstd/Directory.hpp>
88
#include <nstd/Error.hpp>
99
#include <nstd/Process.hpp>
10+
#include <nstd/HashMap.hpp>
11+
12+
#include "HttpRequest.hpp"
1013

1114
Worker::Worker(const Settings& settings, Socket& client, ICallback& callback)
1215
: _settings(settings)
@@ -109,9 +112,9 @@ String getRequestUrl(const String& path)
109112
return result;
110113
}
111114

112-
bool parseGetRequestPath(const String& path, String& url, String& repoUrl, String& repo, String& service)
115+
bool parseGetRequestPath(const String& path, String& repoUrl, String& repo, String& service)
113116
{
114-
url = getRequestUrl(path);
117+
String url = getRequestUrl(path);
115118
const char* x = url.find("/info/refs?service=");
116119
if (!x)
117120
return false;
@@ -125,9 +128,9 @@ bool parseGetRequestPath(const String& path, String& url, String& repoUrl, Strin
125128
}
126129

127130

128-
bool parsePostRequestPath(const String& path, String& url, String& repoUrl, String& repo, String& service)
131+
bool parsePostRequestPath(const String& path, String& repoUrl, String& repo, String& service)
129132
{
130-
url = getRequestUrl(path);
133+
String url = getRequestUrl(path);
131134
if (!url.endsWith("/git-upload-pack"))
132135
return false;
133136
repoUrl = url.substr(0, url.length() - 16);
@@ -138,6 +141,35 @@ bool parsePostRequestPath(const String& path, String& url, String& repoUrl, Stri
138141
return true;
139142
}
140143

144+
bool getAuth(const String& header, String& auth)
145+
{
146+
const char* authStart = header.find("\r\nAuthorization: ");
147+
if (!authStart)
148+
return false;
149+
authStart += 17;
150+
const char* authEnd = String::find(authStart, "\r\n");
151+
if (!authEnd)
152+
return false;
153+
auth = header.substr(authStart - (const char*)header, authEnd - authStart);
154+
return true;
155+
}
156+
157+
bool isAccessible(const String& url, const String& auth)
158+
{
159+
HttpRequest httpRequest;
160+
HashMap<String, String> headerFields;
161+
if (!auth.isEmpty())
162+
headerFields.append("Authorization", auth);
163+
Buffer data;
164+
return httpRequest.get(url, data, headerFields, false);
165+
}
166+
167+
void requestAuth(Socket& client)
168+
{
169+
String response = "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"\"\r\n\r\n";
170+
client.send((const byte*)(const char*)response, response.length());
171+
}
172+
141173
}
142174

143175
void Worker::handleRequest()
@@ -151,29 +183,40 @@ void Worker::handleRequest()
151183
if (!parseHeader(header, method, path))
152184
return;
153185

154-
// todo: auth
155-
// logic?
156-
// if auth is in header check auth with uplink
157-
// else check if uplink can be accessed without auth
158-
// else ? request auth
159-
160-
if (method == "GET")
161-
handleGetRequest(path);
162-
else if (method == "POST")
163-
handlePostRequest(path, body);
164-
}
165-
166-
void Worker::handleGetRequest(const String& path)
167-
{
168-
String requestUrl;
169186
String repoUrl;
170187
String repo;
171188
String service;
172-
if (!parseGetRequestPath(path, requestUrl, repoUrl, repo, service))
189+
if (method == "GET")
190+
{
191+
if (!parseGetRequestPath(path, repoUrl, repo, service))
192+
return;
193+
}
194+
else if (method == "POST")
195+
{
196+
if (!parsePostRequestPath(path, repoUrl, repo, service))
197+
return;
198+
}
199+
else
173200
return;
201+
174202
if (service != "git-upload-pack")
175203
return;
176204

205+
String authCheckUrl = repoUrl + "/info/refs?service=git-upload-pack";
206+
String auth;
207+
getAuth(header, auth);
208+
// todo: auth cache?
209+
if (!isAccessible(authCheckUrl, auth))
210+
return requestAuth(_client);
211+
212+
if (method == "GET")
213+
handleGetRequest(repoUrl, repo);
214+
else if (method == "POST")
215+
handlePostRequest(repo, body);
216+
}
217+
218+
void Worker::handleGetRequest(const String& repoUrl, const String& repo)
219+
{
177220
// todo: avoid concurrent git fetch !
178221

179222
// create cache dir
@@ -215,7 +258,7 @@ void Worker::handleGetRequest(const String& path)
215258
return Log::errorf("Could not launch command '%s': %s", (const char*)command, (const char*)Error::getErrorString());
216259

217260
String response = "HTTP/1.1 200 OK\r\n";
218-
response.append(String("Content-Type: application/x-") + service +"-advertisement\r\n");
261+
response.append("Content-Type: application/x-git-upload-pack-advertisement\r\n");
219262
response.append("Cache-Control: no-cache\r\n\r\n");
220263
response.append("001e# service=git-upload-pack\n0000");
221264
if (_client.send((const byte*)(const char*)response, response.length()) != response.length())
@@ -236,17 +279,8 @@ void Worker::handleGetRequest(const String& path)
236279
}
237280
}
238281

239-
void Worker::handlePostRequest(const String& path, String& body)
282+
void Worker::handlePostRequest(const String& repo, String& body)
240283
{
241-
String requestUrl;
242-
String repoUrl;
243-
String repo;
244-
String service;
245-
if (!parsePostRequestPath(path, requestUrl, repoUrl, repo, service))
246-
return;
247-
if (service != "git-upload-pack")
248-
return;
249-
250284
String cacheDir = _settings.cacheDir + "/" + repo;
251285

252286
{
@@ -261,7 +295,7 @@ void Worker::handlePostRequest(const String& path, String& body)
261295
return;
262296

263297
String response = "HTTP/1.1 200 OK\r\n";
264-
response.append(String("Content-Type: application/x-") + service +"-advertisement\r\n");
298+
response.append("Content-Type: application/x-git-upload-pack-advertisement\r\n");
265299
response.append("Cache-Control: no-cache\r\n\r\n");
266300
if (_client.send((const byte*)(const char*)response, response.length()) != response.length())
267301
return;

src/Worker.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,6 @@ class Worker
3232
private:
3333
uint main();
3434
void handleRequest();
35-
void handleGetRequest(const String& path);
35+
void handleGetRequest(const String& repoUrl, const String& repo);
3636
void handlePostRequest(const String& path, String& body);
3737
};

0 commit comments

Comments
 (0)