Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 97 |
|
0.00% |
0 / 2 |
CRAP | |
0.00% |
0 / 1 |
TestOAuthConsumer | |
0.00% |
0 / 91 |
|
0.00% |
0 / 2 |
342 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 81 |
|
0.00% |
0 / 1 |
306 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\OAuth; |
4 | |
5 | use Maintenance; |
6 | use MediaWiki\Extension\OAuth\Lib\OAuthConsumer; |
7 | use MediaWiki\Extension\OAuth\Lib\OAuthException; |
8 | use MediaWiki\Extension\OAuth\Lib\OAuthRequest; |
9 | use MediaWiki\Extension\OAuth\Lib\OAuthSignatureMethodHmacSha1; |
10 | use MediaWiki\Extension\OAuth\Lib\OAuthSignatureMethodRsaSha1; |
11 | |
12 | /** |
13 | * @ingroup Maintenance |
14 | */ |
15 | if ( getenv( 'MW_INSTALL_PATH' ) ) { |
16 | $IP = getenv( 'MW_INSTALL_PATH' ); |
17 | } else { |
18 | $IP = __DIR__ . '/../../..'; |
19 | } |
20 | |
21 | require_once "$IP/maintenance/Maintenance.php"; |
22 | |
23 | class TestOAuthConsumer extends Maintenance { |
24 | public function __construct() { |
25 | parent::__construct(); |
26 | $this->addDescription( "Test an OAuth consumer" ); |
27 | $this->addOption( 'consumerKey', 'Consumer key', true, true ); |
28 | $this->addOption( 'consumerSecret', 'Consumer secret', false, true ); |
29 | $this->addOption( 'RSAKeyFile', |
30 | 'File containing the RSA private key for the consumer', false, true |
31 | ); |
32 | $this->addOption( 'useSSL', 'Use SSL' ); |
33 | $this->addOption( 'verbose', 'Verbose output (e.g. HTTP request/response headers)' ); |
34 | $this->requireExtension( "OAuth" ); |
35 | } |
36 | |
37 | public function execute() { |
38 | global $wgServer, $wgScriptPath; |
39 | |
40 | $consumerKey = $this->getOption( 'consumerKey' ); |
41 | $consumerSecret = $this->getOption( 'consumerSecret' ); |
42 | $rsaKeyFile = $this->getOption( 'RSAKeyFile' ); |
43 | $baseurl = wfExpandUrl( |
44 | "{$wgServer}{$wgScriptPath}/index.php?title=Special:OAuth", PROTO_CANONICAL ); |
45 | $endpoint = "{$baseurl}/initiate&format=json&oauth_callback=oob"; |
46 | |
47 | $endpoint_acc = "{$baseurl}/token&format=json"; |
48 | |
49 | if ( !$consumerSecret && !$rsaKeyFile ) { |
50 | $this->error( "Either consumerSecret or RSAKeyFile required!" ); |
51 | $this->maybeHelp( true ); |
52 | } |
53 | |
54 | $c = new OAuthConsumer( $consumerKey, $consumerSecret ); |
55 | $parsed = parse_url( $endpoint ); |
56 | $params = []; |
57 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset |
58 | parse_str( $parsed['query'], $params ); |
59 | $req_req = OAuthRequest::from_consumer_and_token( $c, null, "GET", $endpoint, $params ); |
60 | if ( $rsaKeyFile ) { |
61 | try { |
62 | $sig_method = new class ( $rsaKeyFile ) extends OAuthSignatureMethodRsaSha1 { |
63 | /** @var string */ |
64 | private $privKey; |
65 | /** @var string */ |
66 | private $pubKey; |
67 | |
68 | public function __construct( $privKeyFile ) { |
69 | $key = file_get_contents( $privKeyFile ); |
70 | if ( !$key ) { |
71 | throw new OAuthException( "Could not read private key file $privKeyFile" ); |
72 | } |
73 | |
74 | $privKey = openssl_pkey_get_private( $key ); |
75 | if ( !$privKey ) { |
76 | throw new OAuthException( "File $privKeyFile does not contain a private key" ); |
77 | } |
78 | |
79 | $details = openssl_pkey_get_details( $privKey ); |
80 | if ( $details['type'] !== OPENSSL_KEYTYPE_RSA ) { |
81 | throw new OAuthException( "Key is not an RSA key" ); |
82 | } |
83 | if ( !$details['key'] ) { |
84 | throw new OAuthException( "Could not get public key from private key" ); |
85 | } |
86 | |
87 | $this->privKey = $key; |
88 | $this->pubKey = $details['key']; |
89 | } |
90 | |
91 | protected function fetch_public_cert( &$request ) { |
92 | return $this->pubKey; |
93 | } |
94 | |
95 | protected function fetch_private_cert( &$request ) { |
96 | return $this->privKey; |
97 | } |
98 | }; |
99 | } catch ( OAuthException $ex ) { |
100 | $this->fatalError( $ex->getMessage() ); |
101 | } |
102 | } else { |
103 | $sig_method = new OAuthSignatureMethodHmacSha1(); |
104 | } |
105 | $req_req->sign_request( $sig_method, $c, null ); |
106 | |
107 | $this->output( "Calling: $req_req\n" ); |
108 | |
109 | $ch = curl_init(); |
110 | curl_setopt( $ch, CURLOPT_URL, (string)$req_req ); |
111 | if ( $this->hasOption( 'useSSL' ) ) { |
112 | curl_setopt( $ch, CURLOPT_PORT, 443 ); |
113 | } |
114 | if ( $this->hasOption( 'verbose' ) ) { |
115 | curl_setopt( $ch, CURLOPT_VERBOSE, true ); |
116 | } |
117 | curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 ); |
118 | curl_setopt( $ch, CURLOPT_HEADER, 0 ); |
119 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); |
120 | $data = curl_exec( $ch ); |
121 | |
122 | if ( !$data ) { |
123 | $this->output( 'Curl error: ' . curl_error( $ch ) ); |
124 | } |
125 | |
126 | $this->output( "Returned: $data\n\n" ); |
127 | |
128 | $token = json_decode( $data ); |
129 | if ( !$token || !isset( $token->key ) ) { |
130 | $this->fatalError( 'Could not fetch token' ); |
131 | } |
132 | |
133 | $this->output( "Visit $baseurl/authorize" . |
134 | "&oauth_token={$token->key}&oauth_consumer_key=$consumerKey\n" ); |
135 | |
136 | // ACCESS TOKEN |
137 | $this->output( "Enter the verification code:\n" ); |
138 | $fh = fopen( "php://stdin", "r" ); |
139 | $line = fgets( $fh ); |
140 | |
141 | $rc = new OAuthConsumer( $token->key, $token->secret ); |
142 | $parsed = parse_url( $endpoint_acc ); |
143 | // @phan-suppress-next-line PhanTypePossiblyInvalidDimOffset |
144 | parse_str( $parsed['query'], $params ); |
145 | $params['oauth_verifier'] = trim( $line ); |
146 | |
147 | $acc_req = OAuthRequest::from_consumer_and_token( $c, $rc, "GET", $endpoint_acc, $params ); |
148 | $acc_req->sign_request( $sig_method, $c, $rc ); |
149 | |
150 | $this->output( "Calling: $acc_req\n" ); |
151 | |
152 | unset( $ch ); |
153 | $ch = curl_init(); |
154 | curl_setopt( $ch, CURLOPT_URL, (string)$acc_req ); |
155 | if ( $this->hasOption( 'useSSL' ) ) { |
156 | curl_setopt( $ch, CURLOPT_PORT, 443 ); |
157 | } |
158 | if ( $this->hasOption( 'verbose' ) ) { |
159 | curl_setopt( $ch, CURLOPT_VERBOSE, true ); |
160 | } |
161 | curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, 0 ); |
162 | curl_setopt( $ch, CURLOPT_HEADER, 0 ); |
163 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 ); |
164 | $data = curl_exec( $ch ); |
165 | if ( !$data ) { |
166 | $this->output( 'Curl error: ' . curl_error( $ch ) ); |
167 | } |
168 | |
169 | $this->output( "Returned: $data\n\n" ); |
170 | } |
171 | } |
172 | |
173 | $maintClass = TestOAuthConsumer::class; |
174 | require_once RUN_MAINTENANCE_IF_MAIN; |