Rev 1278 | Rev 1297 | Go to most recent revision | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 1278 | Rev 1293 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * OIDplus 2.0 |
4 | * OIDplus 2.0 |
5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2019 - 2023 Daniel Marschall, ViaThinkSoft |
6 | * |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * you may not use this file except in compliance with the License. |
8 | * you may not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
9 | * You may obtain a copy of the License at |
10 | * |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
17 | * limitations under the License. |
18 | */ |
18 | */ |
19 | 19 | ||
20 | // ATTENTION: If you change something, please make sure that the changes |
20 | // ATTENTION: If you change something, please make sure that the changes |
21 | // are synchronous with OIDplusPageAdminRestApi |
21 | // are synchronous with OIDplusPageAdminRestApi |
22 | 22 | ||
23 | namespace ViaThinkSoft\OIDplus; |
23 | namespace ViaThinkSoft\OIDplus; |
24 | 24 | ||
25 | // phpcs:disable PSR1.Files.SideEffects |
25 | // phpcs:disable PSR1.Files.SideEffects |
26 | \defined('INSIDE_OIDPLUS') or die; |
26 | \defined('INSIDE_OIDPLUS') or die; |
27 | // phpcs:enable PSR1.Files.SideEffects |
27 | // phpcs:enable PSR1.Files.SideEffects |
28 | 28 | ||
29 | class OIDplusPageRaRestApi extends OIDplusPagePluginRa { |
29 | class OIDplusPageRaRestApi extends OIDplusPagePluginRa { |
30 | 30 | ||
31 | /** |
31 | /** |
32 | * @param string $actionID |
- | |
33 | * @param array $params |
32 | * @param array $params |
34 | * @return array |
33 | * @return array |
35 | * @throws OIDplusException |
34 | * @throws OIDplusException |
36 | */ |
35 | */ |
37 | public function action(string $actionID, array $params): array { |
36 | private function action_Blacklist(array $params): array { |
38 | if ($actionID == 'blacklistJWT') { |
- | |
39 | if (!OIDplus::baseConfig()->getValue('JWT_ALLOW_REST_USER', true)) { |
37 | if (!OIDplus::baseConfig()->getValue('JWT_ALLOW_REST_USER', true)) { |
40 | throw new OIDplusException(_L('The administrator has disabled this feature. (Base configuration setting %1).','JWT_ALLOW_REST_USER')); |
38 | throw new OIDplusException(_L('The administrator has disabled this feature. (Base configuration setting %1).','JWT_ALLOW_REST_USER')); |
41 | } |
39 | } |
42 | 40 | ||
43 | _CheckParamExists($params, 'user'); |
41 | _CheckParamExists($params, 'user'); |
44 | $ra_email = $params['user']; |
42 | $ra_email = $params['user']; |
45 | 43 | ||
46 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
44 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
47 | throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as the requested RA %2 or as admin.',OIDplus::gui()->link('oidplus:login$ra$'.$ra_email),'<b>'.htmlentities($ra_email).'</b>'), null, 401); |
45 | throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as the requested RA %2 or as admin.',OIDplus::gui()->link('oidplus:login$ra$'.$ra_email),'<b>'.htmlentities($ra_email).'</b>'), null, 401); |
48 | } |
46 | } |
49 | 47 | ||
50 | $gen = OIDplusAuthContentStoreJWT::JWT_GENERATOR_REST; |
48 | $gen = OIDplusAuthContentStoreJWT::JWT_GENERATOR_REST; |
51 | $sub = $ra_email; |
49 | $sub = $ra_email; |
52 | 50 | ||
53 | OIDplusAuthContentStoreJWT::jwtBlacklist($gen, $sub); |
51 | OIDplusAuthContentStoreJWT::jwtBlacklist($gen, $sub); |
54 | 52 | ||
55 | return array("status" => 0); |
53 | return array("status" => 0); |
- | 54 | } |
|
- | 55 | ||
- | 56 | /** |
|
- | 57 | * @param string $actionID |
|
- | 58 | * @param array $params |
|
- | 59 | * @return array |
|
- | 60 | * @throws OIDplusException |
|
- | 61 | */ |
|
- | 62 | public function action(string $actionID, array $params): array { |
|
- | 63 | if ($actionID == 'blacklistJWT') { |
|
- | 64 | return $this->action_Blacklist($params); |
|
56 | } else { |
65 | } else { |
57 | return parent::action($actionID, $params); |
66 | return parent::action($actionID, $params); |
58 | } |
67 | } |
59 | } |
68 | } |
60 | 69 | ||
61 | /** |
70 | /** |
62 | * @param string $id |
71 | * @param string $id |
63 | * @param array $out |
72 | * @param array $out |
64 | * @param bool $handled |
73 | * @param bool $handled |
65 | * @return void |
74 | * @return void |
66 | * @throws OIDplusException |
75 | * @throws OIDplusException |
67 | */ |
76 | */ |
68 | public function gui(string $id, array &$out, bool &$handled) { |
77 | public function gui(string $id, array &$out, bool &$handled) { |
69 | $parts = explode('$',$id,3); |
78 | $parts = explode('$',$id,3); |
70 | $ra_email = $parts[1] ?? ''; |
79 | $ra_email = $parts[1] ?? ''; |
71 | $subpage = $parts[2] ?? ''; |
80 | $subpage = $parts[2] ?? ''; |
72 | 81 | ||
73 | if (empty($ra_email)) return; |
82 | if (empty($ra_email)) return; |
74 | 83 | ||
75 | if ($parts[0] == 'oidplus:rest_api_information_ra') { |
84 | if ($parts[0] == 'oidplus:rest_api_information_ra') { |
76 | $handled = true; |
85 | $handled = true; |
77 | 86 | ||
78 | if (str_starts_with($subpage, 'endpoints:')) { |
87 | if (str_starts_with($subpage, 'endpoints:')) { |
79 | // Note: This page can be accessed WITHOUT login! |
88 | // Note: This page can be accessed WITHOUT login! |
80 | $plugin = OIDplus::getPluginByOid(explode(':',$subpage)[1]); |
89 | $plugin = OIDplus::getPluginByOid(explode(':',$subpage)[1]); |
81 | if (!$plugin || !($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9)) throw new OIDplusException(_L("No endpoints for this plugin found"), null, 404); |
90 | if (!$plugin || !($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9)) throw new OIDplusException(_L("No endpoints for this plugin found"), null, 404); |
82 | $out['title'] = _L('REST API').' - '.$plugin->getManifest()->getName() . ' ' . _L('Endpoints'); |
91 | $out['title'] = _L('REST API').' - '.$plugin->getManifest()->getName() . ' ' . _L('Endpoints'); |
83 | $out['icon'] = file_exists(__DIR__.'/img/endpoints_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/endpoints_icon.png' : ''; |
92 | $out['icon'] = file_exists(__DIR__.'/img/endpoints_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/endpoints_icon.png' : ''; |
84 | $out['text'] = ''; |
93 | $out['text'] = ''; |
85 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
94 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
86 | $out['text'] .= '<p><a '.OIDplus::gui()->link('oidplus:rest_api_information_ra').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Go back').'</a></p>'; |
95 | $out['text'] .= '<p><a '.OIDplus::gui()->link('oidplus:rest_api_information_ra').'><img src="img/arrow_back.png" width="16" alt="'._L('Go back').'"> '._L('Go back').'</a></p>'; |
87 | } |
96 | } |
88 | $out['text'] .= $plugin->restApiInfo('html'); |
97 | $out['text'] .= $plugin->restApiInfo('html'); |
89 | } else { |
98 | } else { |
90 | $out['title'] = _L('REST API'); |
99 | $out['title'] = _L('REST API'); |
91 | $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : ''; |
100 | $out['icon'] = file_exists(__DIR__.'/img/main_icon.png') ? OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon.png' : ''; |
92 | 101 | ||
93 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
102 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) { |
94 | throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as the requested RA %2 or as admin.',OIDplus::gui()->link('oidplus:login$ra$'.$ra_email),'<b>'.htmlentities($ra_email).'</b>'), $out['title'], 401); |
103 | throw new OIDplusHtmlException(_L('You need to <a %1>log in</a> as the requested RA %2 or as admin.',OIDplus::gui()->link('oidplus:login$ra$'.$ra_email),'<b>'.htmlentities($ra_email).'</b>'), $out['title'], 401); |
95 | } |
104 | } |
96 | 105 | ||
97 | if (!OIDplus::baseConfig()->getValue('JWT_ALLOW_REST_USER', true)) { |
106 | if (!OIDplus::baseConfig()->getValue('JWT_ALLOW_REST_USER', true)) { |
98 | throw new OIDplusException(_L('The administrator has disabled this feature. (Base configuration setting %1).','JWT_ALLOW_REST_USER'), $out['title']); |
107 | throw new OIDplusException(_L('The administrator has disabled this feature. (Base configuration setting %1).','JWT_ALLOW_REST_USER'), $out['title']); |
99 | } |
108 | } |
100 | 109 | ||
101 | if (is_null(OIDplus::getPluginByOid("1.3.6.1.4.1.37476.2.5.2.4.1.2"))) { // OIDplusPagePublicRestApi |
110 | if (is_null(OIDplus::getPluginByOid("1.3.6.1.4.1.37476.2.5.2.4.1.2"))) { // OIDplusPagePublicRestApi |
102 | throw new OIDplusException(_L('The administrator has disabled this feature. (Plugin %1).','OIDplusPagePublicRestApi'), $out['title']); |
111 | throw new OIDplusException(_L('The administrator has disabled this feature. (Plugin %1).','OIDplusPagePublicRestApi'), $out['title']); |
103 | } |
112 | } |
104 | 113 | ||
105 | $gen = OIDplusAuthContentStoreJWT::JWT_GENERATOR_REST; |
114 | $gen = OIDplusAuthContentStoreJWT::JWT_GENERATOR_REST; |
106 | $sub = $ra_email; |
115 | $sub = $ra_email; |
107 | 116 | ||
108 | $authSimulation = new OIDplusAuthContentStoreJWT(); |
117 | $authSimulation = new OIDplusAuthContentStoreJWT(); |
109 | $authSimulation->raLogin($ra_email); |
118 | $authSimulation->raLogin($ra_email); |
110 | $authSimulation->setValue('oidplus_generator', $gen); |
119 | $authSimulation->setValue('oidplus_generator', $gen); |
111 | $token = $authSimulation->getJWTToken(); |
120 | $token = $authSimulation->getJWTToken(); |
112 | 121 | ||
113 | $out['text'] .= '<p>'._L('You can make automated calls to your OIDplus account by calling an REST API.').'</p>'; |
122 | $out['text'] .= '<p>'._L('You can make automated calls to your OIDplus account by calling an REST API.').'</p>'; |
114 | $out['text'] .= '<h2>'._L('Endpoints').'</h2>'; |
123 | $out['text'] .= '<h2>'._L('Endpoints').'</h2>'; |
115 | $endpoints = ''; |
124 | $endpoints = ''; |
116 | foreach (OIDplus::getAllPlugins() as $plugin) { |
125 | foreach (OIDplus::getAllPlugins() as $plugin) { |
117 | if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9) { |
126 | if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9) { |
118 | $link = 'oidplus:rest_api_information_ra$'.$ra_email.'$endpoints:'.$plugin->getManifest()->getOid(); |
127 | $link = 'oidplus:rest_api_information_ra$'.$ra_email.'$endpoints:'.$plugin->getManifest()->getOid(); |
119 | $endpoints .= '<li><a '.OIDplus::gui()->link($link).'>'.htmlentities($plugin->getManifest()->getName()).'</a></li>'; |
128 | $endpoints .= '<li><a '.OIDplus::gui()->link($link).'>'.htmlentities($plugin->getManifest()->getName()).'</a></li>'; |
120 | } |
129 | } |
121 | } |
130 | } |
122 | if ($endpoints) { |
131 | if ($endpoints) { |
123 | $out['text'] .= '<p>'._L('The following installed plugins are offering REST endpoints:').'</p>'; |
132 | $out['text'] .= '<p>'._L('The following installed plugins are offering REST endpoints:').'</p>'; |
124 | $out['text'] .= '<p><ul>'.$endpoints.'</ul></p>'; |
133 | $out['text'] .= '<p><ul>'.$endpoints.'</ul></p>'; |
125 | } else { |
134 | } else { |
126 | $out['text'] .= '<p>'._L('No installed plugin offers a REST functionality').'</p>'; |
135 | $out['text'] .= '<p>'._L('No installed plugin offers a REST functionality').'</p>'; |
127 | } |
136 | } |
128 | $out['text'] .= '<h2>'._L('Authentication').'</h2>'; |
137 | $out['text'] .= '<h2>'._L('Authentication').'</h2>'; |
129 | $out['text'] .= '<p>'._L('The authentication is done via the following HTTP header:').'</p>'; |
138 | $out['text'] .= '<p>'._L('The authentication is done via the following HTTP header:').'</p>'; |
130 | $out['text'] .= '<p><pre id="oidplus_auth_jwt">'; |
139 | $out['text'] .= '<p><pre id="oidplus_auth_jwt">'; |
131 | $out['text'] .= 'Authentication: Bearer '.htmlentities($token)."\n"; |
140 | $out['text'] .= 'Authentication: Bearer '.htmlentities($token)."\n"; |
132 | $out['text'] .= '</pre></p>'; |
141 | $out['text'] .= '</pre></p>'; |
133 | $out['text'] .= '<p><input type="button" value="'._L('Copy to clipboard').'" onClick="copyToClipboard(oidplus_auth_jwt)"></p>'; |
142 | $out['text'] .= '<p><input type="button" value="'._L('Copy to clipboard').'" onClick="copyToClipboard(oidplus_auth_jwt)"></p>'; |
134 | $out['text'] .= '<p>'._L('Please keep this information confidential!').'</p>'; |
143 | $out['text'] .= '<p>'._L('Please keep this information confidential!').'</p>'; |
135 | $out['text'] .= '<p>'._L('The JWT-token (secret!) will automatically perform a one-time-login to fulfill the request. The other fields are the normal fields which are called during the usual operation of OIDplus.').'</p>'; |
144 | $out['text'] .= '<p>'._L('The JWT-token (secret!) will automatically perform a one-time-login to fulfill the request. The other fields are the normal fields which are called during the usual operation of OIDplus.').'</p>'; |
136 | 145 | ||
137 | $out['text'] .= '<h2>'._L('Blacklisted tokens').'</h2>'; |
146 | $out['text'] .= '<h2>'._L('Blacklisted tokens').'</h2>'; |
138 | $bl_time = OIDplusAuthContentStoreJWT::jwtGetBlacklistTime($gen, $sub); |
147 | $bl_time = OIDplusAuthContentStoreJWT::jwtGetBlacklistTime($gen, $sub); |
139 | if ($bl_time == 0) { |
148 | if ($bl_time == 0) { |
140 | $out['text'] .= '<p>'._L('None of the previously generated JWT tokens have been blacklisted.').'</p>'; |
149 | $out['text'] .= '<p>'._L('None of the previously generated JWT tokens have been blacklisted.').'</p>'; |
141 | } else { |
150 | } else { |
142 | $out['text'] .= '<p>'._L('All tokens generated before %1 have been blacklisted.',date('d F Y, H:i:s',$bl_time+1)).'</p>'; |
151 | $out['text'] .= '<p>'._L('All tokens generated before %1 have been blacklisted.',date('d F Y, H:i:s',$bl_time+1)).'</p>'; |
143 | } |
152 | } |
144 | $out['text'] .= '<button type="button" name="btn_blacklist_jwt" id="btn_blacklist_jwt" class="btn btn-danger btn-xs" onclick="OIDplusPageRaRestApi.blacklistJWT('.js_escape($ra_email).')">'._L('Blacklist all previously generated tokens').'</button>'; |
153 | $out['text'] .= '<button type="button" name="btn_blacklist_jwt" id="btn_blacklist_jwt" class="btn btn-danger btn-xs" onclick="OIDplusPageRaRestApi.blacklistJWT('.js_escape($ra_email).')">'._L('Blacklist all previously generated tokens').'</button>'; |
145 | } |
154 | } |
146 | } |
155 | } |
147 | } |
156 | } |
148 | 157 | ||
149 | /** |
158 | /** |
150 | * @param array $json |
159 | * @param array $json |
151 | * @param string|null $ra_email |
160 | * @param string|null $ra_email |
152 | * @param bool $nonjs |
161 | * @param bool $nonjs |
153 | * @param string $req_goto |
162 | * @param string $req_goto |
154 | * @return bool |
163 | * @return bool |
155 | * @throws OIDplusException |
164 | * @throws OIDplusException |
156 | */ |
165 | */ |
157 | public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool { |
166 | public function tree(array &$json, string $ra_email=null, bool $nonjs=false, string $req_goto=''): bool { |
158 | if (!$ra_email) return false; |
167 | if (!$ra_email) return false; |
159 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) return false; |
168 | if (!OIDplus::authUtils()->isRaLoggedIn($ra_email) && !OIDplus::authUtils()->isAdminLoggedIn()) return false; |
160 | 169 | ||
161 | if (file_exists(__DIR__.'/img/main_icon16.png')) { |
170 | if (file_exists(__DIR__.'/img/main_icon16.png')) { |
162 | $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png'; |
171 | $tree_icon = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/main_icon16.png'; |
163 | } else { |
172 | } else { |
164 | $tree_icon = null; // default icon (folder) |
173 | $tree_icon = null; // default icon (folder) |
165 | } |
174 | } |
166 | 175 | ||
167 | if (file_exists(__DIR__.'/img/endpoints_icon16.png')) { |
176 | if (file_exists(__DIR__.'/img/endpoints_icon16.png')) { |
168 | $tree_icon_endpoints = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/endpoints_icon16.png'; |
177 | $tree_icon_endpoints = OIDplus::webpath(__DIR__,OIDplus::PATH_RELATIVE).'img/endpoints_icon16.png'; |
169 | } else { |
178 | } else { |
170 | $tree_icon_endpoints = null; // default icon (folder) |
179 | $tree_icon_endpoints = null; // default icon (folder) |
171 | } |
180 | } |
172 | 181 | ||
173 | $submenu = array(); |
182 | $submenu = array(); |
174 | foreach (OIDplus::getAllPlugins() as $plugin) { |
183 | foreach (OIDplus::getAllPlugins() as $plugin) { |
175 | if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9) { |
184 | if ($plugin instanceof INTF_OID_1_3_6_1_4_1_37476_2_5_2_3_9) { |
176 | $submenu[] = [ |
185 | $submenu[] = [ |
177 | 'id' => 'oidplus:rest_api_information_ra$'.$ra_email.'$endpoints:'.$plugin->getManifest()->getOid(), |
186 | 'id' => 'oidplus:rest_api_information_ra$'.$ra_email.'$endpoints:'.$plugin->getManifest()->getOid(), |
178 | 'icon' => $tree_icon_endpoints, |
187 | 'icon' => $tree_icon_endpoints, |
179 | 'text' => $plugin->getManifest()->getName() . ' ' . _L('Endpoints') |
188 | 'text' => $plugin->getManifest()->getName() . ' ' . _L('Endpoints') |
180 | ]; |
189 | ]; |
181 | } |
190 | } |
182 | } |
191 | } |
183 | 192 | ||
184 | $json[] = array( |
193 | $json[] = array( |
185 | 'id' => 'oidplus:rest_api_information_ra$'.$ra_email, |
194 | 'id' => 'oidplus:rest_api_information_ra$'.$ra_email, |
186 | 'icon' => $tree_icon, |
195 | 'icon' => $tree_icon, |
187 | 'text' => _L('REST API'), |
196 | 'text' => _L('REST API'), |
188 | 'children' => $submenu |
197 | 'children' => $submenu |
189 | ); |
198 | ); |
190 | 199 | ||
191 | return true; |
200 | return true; |
192 | } |
201 | } |
193 | 202 | ||
194 | /** |
203 | /** |
195 | * @param string $request |
204 | * @param string $request |
196 | * @return array|false |
205 | * @return array|false |
197 | */ |
206 | */ |
198 | public function tree_search(string $request) { |
207 | public function tree_search(string $request) { |
199 | return false; |
208 | return false; |
200 | } |
209 | } |
201 | } |
210 | } |
202 | 211 |