Skip to content

Commit 36463e3

Browse files
chriscoolgitster
authored andcommitted
promisor-remote: check advertised name or URL
A previous commit introduced a "promisor.acceptFromServer" configuration variable with only "None" or "All" as valid values. Let's introduce "KnownName" and "KnownUrl" as valid values for this configuration option to give more choice to a client about which promisor remotes it might accept among those that the server advertised. In case of "KnownName", the client will accept promisor remotes which are already configured on the client and have the same name as those advertised by the client. This could be useful in a corporate setup where servers and clients are trusted to not switch names and URLs, but where some kind of control is still useful. In case of "KnownUrl", the client will accept promisor remotes which have both the same name and the same URL configured on the client as the name and URL advertised by the server. This is the most secure option, so it should be used if possible. Signed-off-by: Christian Couder <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d460267 commit 36463e3

File tree

3 files changed

+138
-12
lines changed

3 files changed

+138
-12
lines changed

Documentation/config/promisor.adoc

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@ promisor.advertise::
1212
promisor.acceptFromServer::
1313
If set to "all", a client will accept all the promisor remotes
1414
a server might advertise using the "promisor-remote"
15-
capability. Default is "none", which means no promisor remote
16-
advertised by a server will be accepted. By accepting a
17-
promisor remote, the client agrees that the server might omit
18-
objects that are lazily fetchable from this promisor remote
19-
from its responses to "fetch" and "clone" requests from the
20-
client. See linkgit:gitprotocol-v2[5].
15+
capability. If set to "knownName" the client will accept
16+
promisor remotes which are already configured on the client
17+
and have the same name as those advertised by the client. This
18+
is not very secure, but could be used in a corporate setup
19+
where servers and clients are trusted to not switch name and
20+
URLs. If set to "knownUrl", the client will accept promisor
21+
remotes which have both the same name and the same URL
22+
configured on the client as the name and URL advertised by the
23+
server. This is more secure than "all" or "knownName", so it
24+
should be used if possible instead of those options. Default
25+
is "none", which means no promisor remote advertised by a
26+
server will be accepted. By accepting a promisor remote, the
27+
client agrees that the server might omit objects that are
28+
lazily fetchable from this promisor remote from its responses
29+
to "fetch" and "clone" requests from the client. See
30+
linkgit:gitprotocol-v2[5].

promisor-remote.c

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -368,30 +368,73 @@ char *promisor_remote_info(struct repository *repo)
368368
return strbuf_detach(&sb, NULL);
369369
}
370370

371+
/*
372+
* Find first index of 'nicks' where there is 'nick'. 'nick' is
373+
* compared case insensitively to the strings in 'nicks'. If not found
374+
* 'nicks->nr' is returned.
375+
*/
376+
static size_t remote_nick_find(struct strvec *nicks, const char *nick)
377+
{
378+
for (size_t i = 0; i < nicks->nr; i++)
379+
if (!strcasecmp(nicks->v[i], nick))
380+
return i;
381+
return nicks->nr;
382+
}
383+
371384
enum accept_promisor {
372385
ACCEPT_NONE = 0,
386+
ACCEPT_KNOWN_URL,
387+
ACCEPT_KNOWN_NAME,
373388
ACCEPT_ALL
374389
};
375390

376391
static int should_accept_remote(enum accept_promisor accept,
377-
const char *remote_name UNUSED,
378-
const char *remote_url UNUSED)
392+
const char *remote_name, const char *remote_url,
393+
struct strvec *names, struct strvec *urls)
379394
{
395+
size_t i;
396+
380397
if (accept == ACCEPT_ALL)
381398
return 1;
382399

383-
BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
400+
i = remote_nick_find(names, remote_name);
401+
402+
if (i >= names->nr)
403+
/* We don't know about that remote */
404+
return 0;
405+
406+
if (accept == ACCEPT_KNOWN_NAME)
407+
return 1;
408+
409+
if (accept != ACCEPT_KNOWN_URL)
410+
BUG("Unhandled 'enum accept_promisor' value '%d'", accept);
411+
412+
if (!strcmp(urls->v[i], remote_url))
413+
return 1;
414+
415+
warning(_("known remote named '%s' but with url '%s' instead of '%s'"),
416+
remote_name, urls->v[i], remote_url);
417+
418+
return 0;
384419
}
385420

386-
static void filter_promisor_remote(struct strvec *accepted, const char *info)
421+
static void filter_promisor_remote(struct repository *repo,
422+
struct strvec *accepted,
423+
const char *info)
387424
{
388425
struct strbuf **remotes;
389426
const char *accept_str;
390427
enum accept_promisor accept = ACCEPT_NONE;
428+
struct strvec names = STRVEC_INIT;
429+
struct strvec urls = STRVEC_INIT;
391430

392431
if (!git_config_get_string_tmp("promisor.acceptfromserver", &accept_str)) {
393432
if (!*accept_str || !strcasecmp("None", accept_str))
394433
accept = ACCEPT_NONE;
434+
else if (!strcasecmp("KnownUrl", accept_str))
435+
accept = ACCEPT_KNOWN_URL;
436+
else if (!strcasecmp("KnownName", accept_str))
437+
accept = ACCEPT_KNOWN_NAME;
395438
else if (!strcasecmp("All", accept_str))
396439
accept = ACCEPT_ALL;
397440
else
@@ -402,6 +445,9 @@ static void filter_promisor_remote(struct strvec *accepted, const char *info)
402445
if (accept == ACCEPT_NONE)
403446
return;
404447

448+
if (accept != ACCEPT_ALL)
449+
promisor_info_vecs(repo, &names, &urls);
450+
405451
/* Parse remote info received */
406452

407453
remotes = strbuf_split_str(info, ';', 0);
@@ -431,14 +477,16 @@ static void filter_promisor_remote(struct strvec *accepted, const char *info)
431477
if (remote_url)
432478
decoded_url = url_percent_decode(remote_url);
433479

434-
if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url))
480+
if (decoded_name && should_accept_remote(accept, decoded_name, decoded_url, &names, &urls))
435481
strvec_push(accepted, decoded_name);
436482

437483
strbuf_list_free(elems);
438484
free(decoded_name);
439485
free(decoded_url);
440486
}
441487

488+
strvec_clear(&names);
489+
strvec_clear(&urls);
442490
strbuf_list_free(remotes);
443491
}
444492

@@ -447,7 +495,7 @@ char *promisor_remote_reply(const char *info)
447495
struct strvec accepted = STRVEC_INIT;
448496
struct strbuf reply = STRBUF_INIT;
449497

450-
filter_promisor_remote(&accepted, info);
498+
filter_promisor_remote(the_repository, &accepted, info);
451499

452500
if (!accepted.nr)
453501
return NULL;

t/t5710-promisor-remote-capability.sh

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,74 @@ test_expect_success "init + fetch with promisor.advertise set to 'true'" '
160160
check_missing_objects server 1 "$oid"
161161
'
162162

163+
test_expect_success "clone with promisor.acceptfromserver set to 'KnownName'" '
164+
git -C server config promisor.advertise true &&
165+
166+
# Clone from server to create a client
167+
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
168+
-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
169+
-c remote.lop.url="file://$(pwd)/lop" \
170+
-c promisor.acceptfromserver=KnownName \
171+
--no-local --filter="blob:limit=5k" server client &&
172+
test_when_finished "rm -rf client" &&
173+
174+
# Check that the largest object is still missing on the server
175+
check_missing_objects server 1 "$oid"
176+
'
177+
178+
test_expect_success "clone with 'KnownName' and different remote names" '
179+
git -C server config promisor.advertise true &&
180+
181+
# Clone from server to create a client
182+
GIT_NO_LAZY_FETCH=0 git clone -c remote.serverTwo.promisor=true \
183+
-c remote.serverTwo.fetch="+refs/heads/*:refs/remotes/lop/*" \
184+
-c remote.serverTwo.url="file://$(pwd)/lop" \
185+
-c promisor.acceptfromserver=KnownName \
186+
--no-local --filter="blob:limit=5k" server client &&
187+
test_when_finished "rm -rf client" &&
188+
189+
# Check that the largest object is not missing on the server
190+
check_missing_objects server 0 "" &&
191+
192+
# Reinitialize server so that the largest object is missing again
193+
initialize_server 1 "$oid"
194+
'
195+
196+
test_expect_success "clone with promisor.acceptfromserver set to 'KnownUrl'" '
197+
git -C server config promisor.advertise true &&
198+
199+
# Clone from server to create a client
200+
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
201+
-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
202+
-c remote.lop.url="file://$(pwd)/lop" \
203+
-c promisor.acceptfromserver=KnownUrl \
204+
--no-local --filter="blob:limit=5k" server client &&
205+
test_when_finished "rm -rf client" &&
206+
207+
# Check that the largest object is still missing on the server
208+
check_missing_objects server 1 "$oid"
209+
'
210+
211+
test_expect_success "clone with 'KnownUrl' and different remote urls" '
212+
ln -s lop serverTwo &&
213+
214+
git -C server config promisor.advertise true &&
215+
216+
# Clone from server to create a client
217+
GIT_NO_LAZY_FETCH=0 git clone -c remote.lop.promisor=true \
218+
-c remote.lop.fetch="+refs/heads/*:refs/remotes/lop/*" \
219+
-c remote.lop.url="file://$(pwd)/serverTwo" \
220+
-c promisor.acceptfromserver=KnownUrl \
221+
--no-local --filter="blob:limit=5k" server client &&
222+
test_when_finished "rm -rf client" &&
223+
224+
# Check that the largest object is not missing on the server
225+
check_missing_objects server 0 "" &&
226+
227+
# Reinitialize server so that the largest object is missing again
228+
initialize_server 1 "$oid"
229+
'
230+
163231
test_expect_success "clone with promisor.advertise set to 'true' but don't delete the client" '
164232
git -C server config promisor.advertise true &&
165233

0 commit comments

Comments
 (0)