forked from phacility/phabricator
/
PhabricatorAuthProviderOAuth1JIRA.php
286 lines (237 loc) · 8.97 KB
/
PhabricatorAuthProviderOAuth1JIRA.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
<?php
final class PhabricatorAuthProviderOAuth1JIRA
extends PhabricatorAuthProviderOAuth1 {
public function getJIRABaseURI() {
return $this->getProviderConfig()->getProperty(self::PROPERTY_JIRA_URI);
}
public function getProviderName() {
return pht('JIRA');
}
public function getDescriptionForCreate() {
return pht('Configure JIRA OAuth. NOTE: Only supports JIRA 6.');
}
public function getConfigurationHelp() {
if ($this->isSetup()) {
return pht(
"**Step 1 of 2**: Provide the name and URI for your JIRA install.\n\n".
"In the next step, you will configure JIRA.");
} else {
$login_uri = $this->getLoginURI();
return pht(
"**Step 2 of 2**: In this step, you will configure JIRA.\n\n".
"**Create a JIRA Application**: Log into JIRA and go to ".
"**Administration**, then **Add-ons**, then **Application Links**. ".
"Click the button labeled **Add Application Link**, and use these ".
"settings to create an application:\n\n".
" - **Server URL**: `%s`\n".
" - Then, click **Next**. On the second page:\n".
" - **Application Name**: `Phabricator`\n".
" - **Application Type**: `Generic Application`\n".
" - Then, click **Create**.\n\n".
"**Configure Your Application**: Find the application you just ".
"created in the table, and click the **Configure** link under ".
"**Actions**. Select **Incoming Authentication** and click the ".
"**OAuth** tab (it may be selected by default). Then, use these ".
"settings:\n\n".
" - **Consumer Key**: Set this to the \"Consumer Key\" value in the ".
"form above.\n".
" - **Consumer Name**: `Phabricator`\n".
" - **Public Key**: Set this to the \"Public Key\" value in the ".
"form above.\n".
" - **Consumer Callback URL**: `%s`\n".
"Click **Save** in JIRA. Authentication should now be configured, ".
"and this provider should work correctly.",
PhabricatorEnv::getProductionURI('/'),
$login_uri);
}
}
protected function newOAuthAdapter() {
$config = $this->getProviderConfig();
return id(new PhutilAuthAdapterOAuthJIRA())
->setAdapterDomain($config->getProviderDomain())
->setJIRABaseURI($config->getProperty(self::PROPERTY_JIRA_URI))
->setPrivateKey(
new PhutilOpaqueEnvelope(
$config->getProperty(self::PROPERTY_PRIVATE_KEY)));
}
protected function getLoginIcon() {
return 'Jira';
}
private function isSetup() {
return !$this->getProviderConfig()->getID();
}
const PROPERTY_JIRA_NAME = 'oauth1:jira:name';
const PROPERTY_JIRA_URI = 'oauth1:jira:uri';
const PROPERTY_PUBLIC_KEY = 'oauth1:jira:key:public';
const PROPERTY_PRIVATE_KEY = 'oauth1:jira:key:private';
public function readFormValuesFromProvider() {
$config = $this->getProviderConfig();
$uri = $config->getProperty(self::PROPERTY_JIRA_URI);
return array(
self::PROPERTY_JIRA_NAME => $this->getProviderDomain(),
self::PROPERTY_JIRA_URI => $uri,
);
}
public function readFormValuesFromRequest(AphrontRequest $request) {
$is_setup = $this->isSetup();
if ($is_setup) {
$name = $request->getStr(self::PROPERTY_JIRA_NAME);
} else {
$name = $this->getProviderDomain();
}
return array(
self::PROPERTY_JIRA_NAME => $name,
self::PROPERTY_JIRA_URI => $request->getStr(self::PROPERTY_JIRA_URI),
);
}
public function processEditForm(
AphrontRequest $request,
array $values) {
$errors = array();
$issues = array();
$is_setup = $this->isSetup();
$key_name = self::PROPERTY_JIRA_NAME;
$key_uri = self::PROPERTY_JIRA_URI;
if (!strlen($values[$key_name])) {
$errors[] = pht('JIRA instance name is required.');
$issues[$key_name] = pht('Required');
} else if (!preg_match('/^[a-z0-9.]+$/', $values[$key_name])) {
$errors[] = pht(
'JIRA instance name must contain only lowercase letters, digits, and '.
'period.');
$issues[$key_name] = pht('Invalid');
}
if (!strlen($values[$key_uri])) {
$errors[] = pht('JIRA base URI is required.');
$issues[$key_uri] = pht('Required');
} else {
$uri = new PhutilURI($values[$key_uri]);
if (!$uri->getProtocol()) {
$errors[] = pht(
'JIRA base URI should include protocol (like "https://").');
$issues[$key_uri] = pht('Invalid');
}
}
if (!$errors && $is_setup) {
$config = $this->getProviderConfig();
$config->setProviderDomain($values[$key_name]);
$consumer_key = 'phjira.'.Filesystem::readRandomCharacters(16);
list($public, $private) = PhutilAuthAdapterOAuthJIRA::newJIRAKeypair();
$config->setProperty(self::PROPERTY_PUBLIC_KEY, $public);
$config->setProperty(self::PROPERTY_PRIVATE_KEY, $private);
$config->setProperty(self::PROPERTY_CONSUMER_KEY, $consumer_key);
}
return array($errors, $issues, $values);
}
public function extendEditForm(
AphrontRequest $request,
AphrontFormView $form,
array $values,
array $issues) {
if (!function_exists('openssl_pkey_new')) {
// TODO: This could be a bit prettier.
throw new Exception(
pht(
"The PHP 'openssl' extension is not installed. You must install ".
"this extension in order to add a JIRA authentication provider, ".
"because JIRA OAuth requests use the RSA-SHA1 signing algorithm. ".
"Install the 'openssl' extension, restart your webserver, and try ".
"again."));
}
$form->appendRemarkupInstructions(
pht(
'NOTE: This provider **only supports JIRA 6**. It will not work with '.
'JIRA 5 or earlier.'));
$is_setup = $this->isSetup();
$e_required = $request->isFormPost() ? null : true;
$v_name = $values[self::PROPERTY_JIRA_NAME];
if ($is_setup) {
$e_name = idx($issues, self::PROPERTY_JIRA_NAME, $e_required);
} else {
$e_name = null;
}
$v_uri = $values[self::PROPERTY_JIRA_URI];
$e_uri = idx($issues, self::PROPERTY_JIRA_URI, $e_required);
if ($is_setup) {
$form
->appendRemarkupInstructions(
pht(
"**JIRA Instance Name**\n\n".
"Choose a permanent name for this instance of JIRA. Phabricator ".
"uses this name internally to keep track of this instance of ".
"JIRA, in case the URL changes later.\n\n".
"Use lowercase letters, digits, and period. For example, ".
"`jira`, `jira.mycompany` or `jira.engineering` are reasonable ".
"names."))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('JIRA Instance Name'))
->setValue($v_name)
->setName(self::PROPERTY_JIRA_NAME)
->setError($e_name));
} else {
$form
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('JIRA Instance Name'))
->setValue($v_name));
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('JIRA Base URI'))
->setValue($v_uri)
->setName(self::PROPERTY_JIRA_URI)
->setCaption(
pht(
'The URI where JIRA is installed. For example: %s',
phutil_tag('tt', array(), 'https://jira.mycompany.com/')))
->setError($e_uri));
if (!$is_setup) {
$config = $this->getProviderConfig();
$ckey = $config->getProperty(self::PROPERTY_CONSUMER_KEY);
$ckey = phutil_tag('tt', array(), $ckey);
$pkey = $config->getProperty(self::PROPERTY_PUBLIC_KEY);
$pkey = phutil_escape_html_newlines($pkey);
$pkey = phutil_tag('tt', array(), $pkey);
$form
->appendRemarkupInstructions(
pht(
'NOTE: **To complete setup**, copy and paste these keys into JIRA '.
'according to the instructions below.'))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Consumer Key'))
->setValue($ckey))
->appendChild(
id(new AphrontFormStaticControl())
->setLabel(pht('Public Key'))
->setValue($pkey));
}
}
/**
* JIRA uses a setup step to generate public/private keys.
*/
public function hasSetupStep() {
return true;
}
public static function getJIRAProvider() {
$providers = self::getAllEnabledProviders();
foreach ($providers as $provider) {
if ($provider instanceof PhabricatorAuthProviderOAuth1JIRA) {
return $provider;
}
}
return null;
}
public function newJIRAFuture(
PhabricatorExternalAccount $account,
$path,
$method,
$params = array()) {
$adapter = clone $this->getAdapter();
$adapter->setToken($account->getProperty('oauth1.token'));
$adapter->setTokenSecret($account->getProperty('oauth1.token.secret'));
return $adapter->newJIRAFuture($path, $method, $params);
}
}