forked from phacility/phabricator
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAphrontRedirectResponse.php
166 lines (138 loc) · 5.21 KB
/
AphrontRedirectResponse.php
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
<?php
/**
* TODO: Should be final but isn't because of AphrontReloadResponse.
*/
class AphrontRedirectResponse extends AphrontResponse {
private $uri;
private $stackWhenCreated;
private $isExternal;
public function setIsExternal($external) {
$this->isExternal = $external;
return $this;
}
public function __construct() {
if ($this->shouldStopForDebugging()) {
// If we're going to stop, capture the stack so we can print it out.
$this->stackWhenCreated = id(new Exception())->getTrace();
}
}
public function setURI($uri) {
$this->uri = $uri;
return $this;
}
public function getURI() {
// NOTE: When we convert a RedirectResponse into an AjaxResponse, we pull
// the URI through this method. Make sure it passes checks before we
// hand it over to callers.
return self::getURIForRedirect($this->uri, $this->isExternal);
}
public function shouldStopForDebugging() {
return PhabricatorEnv::getEnvConfig('debug.stop-on-redirect');
}
public function getHeaders() {
$headers = array();
if (!$this->shouldStopForDebugging()) {
$uri = self::getURIForRedirect($this->uri, $this->isExternal);
$headers[] = array('Location', $uri);
}
$headers = array_merge(parent::getHeaders(), $headers);
return $headers;
}
public function buildResponseString() {
if ($this->shouldStopForDebugging()) {
$request = $this->getRequest();
$viewer = $request->getUser();
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->setApplicationName(pht('Debug'));
$view->setTitle(pht('Stopped on Redirect'));
$dialog = new AphrontDialogView();
$dialog->setUser($viewer);
$dialog->setTitle(pht('Stopped on Redirect'));
$dialog->appendParagraph(
pht(
'You were stopped here because %s is set in your configuration.',
phutil_tag('tt', array(), 'debug.stop-on-redirect')));
$dialog->appendParagraph(
pht(
'You are being redirected to: %s',
phutil_tag('tt', array(), $this->getURI())));
$dialog->addCancelButton($this->getURI(), pht('Continue'));
$dialog->appendChild(phutil_tag('br'));
$dialog->appendChild(
id(new AphrontStackTraceView())
->setUser($viewer)
->setTrace($this->stackWhenCreated));
$dialog->setIsStandalone(true);
$dialog->setWidth(AphrontDialogView::WIDTH_FULL);
$box = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($dialog);
$view->appendChild($box);
return $view->render();
}
return '';
}
/**
* Format a URI for use in a "Location:" header.
*
* Verifies that a URI redirects to the expected type of resource (local or
* remote) and formats it for use in a "Location:" header.
*
* The HTTP spec says "Location:" headers must use absolute URIs. Although
* browsers work with relative URIs, we return absolute URIs to avoid
* ambiguity. For example, Chrome interprets "Location: /\evil.com" to mean
* "perform a protocol-relative redirect to evil.com".
*
* @param string URI to redirect to.
* @param bool True if this URI identifies a remote resource.
* @return string URI for use in a "Location:" header.
*/
public static function getURIForRedirect($uri, $is_external) {
$uri_object = new PhutilURI($uri);
if ($is_external) {
// If this is a remote resource it must have a domain set. This
// would also be caught below, but testing for it explicitly first allows
// us to raise a better error message.
if (!strlen($uri_object->getDomain())) {
throw new Exception(
pht(
'Refusing to redirect to external URI "%s". This URI '.
'is not fully qualified, and is missing a domain name. To '.
'redirect to a local resource, remove the external flag.',
(string)$uri));
}
// Check that it's a valid remote resource.
if (!PhabricatorEnv::isValidURIForLink($uri)) {
throw new Exception(
pht(
'Refusing to redirect to external URI "%s". This URI '.
'is not a valid remote web resource.',
(string)$uri));
}
} else {
// If this is a local resource, it must not have a domain set. This allows
// us to raise a better error message than the check below can.
if (strlen($uri_object->getDomain())) {
throw new Exception(
pht(
'Refusing to redirect to local resource "%s". The URI has a '.
'domain, but the redirect is not marked external. Mark '.
'redirects as external to allow redirection off the local '.
'domain.',
(string)$uri));
}
// If this is a local resource, it must be a valid local resource.
if (!PhabricatorEnv::isValidLocalURIForLink($uri)) {
throw new Exception(
pht(
'Refusing to redirect to local resource "%s". This URI is not '.
'formatted in a recognizable way.',
(string)$uri));
}
// Fully qualify the result URI.
$uri = PhabricatorEnv::getURI((string)$uri);
}
return (string)$uri;
}
}