Skip to content

Commit

Permalink
misc stuff (openemr#4132)
Browse files Browse the repository at this point in the history
  • Loading branch information
bradymiller authored Dec 30, 2020
1 parent f65a732 commit fd32973
Show file tree
Hide file tree
Showing 10 changed files with 82 additions and 59 deletions.
10 changes: 6 additions & 4 deletions API_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,6 @@ Finally, APIs which are integrated with the new `handleProcessingResult` method

Enable the Standard API service (/api/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Standard REST API"

Enable the Patient Portal API service (/portal/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Patient Portal REST API"

### Using API Internally

There are several ways to make API calls from an authorized session and maintain security:
Expand Down Expand Up @@ -170,10 +168,10 @@ This is a listing of scopes:
- `user/Practitioner.write`
- `user/PractitionerRole.read`
- `user/Procedure.read`
- `api:port` (patient api which are the /portal/ endpoints)
- `api:port` (patient api which are the /portal/ endpoints) (EXPERIMENTAL)
- `patient/encounter.read`
- `patient/patient.read`
- `api:pofh` (patient fhir which are the /portalfhir/ endpoints)
- `api:pofh` (patient fhir which are the /portalfhir/ endpoints) (EXPERIMENTAL)
- `patient/Encounter.read`
- `patient/Patient.read`

Expand Down Expand Up @@ -1631,6 +1629,10 @@ curl -X DELETE 'http://localhost:8300/apis/default/api/patient/1/message/1'

### /portal/ Endpoints

This is under development and is considered EXPERIMENTAL.

Enable the Patient Portal API service (/portal/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Patient Portal REST API (EXPERIMENTAL)"

OpenEMR patient portal endpoints Use `http://localhost:8300/apis/default/portal as base URI.`

Note that the `default` component can be changed to the name of the site when using OpenEMR's multisite feature.
Expand Down
6 changes: 4 additions & 2 deletions FHIR_README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ Database Result -> Service Component -> FHIR Service Component -> Parse OpenEMR

Enable the Standard FHIR service (/fhir/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Standard FHIR REST API"

Enable the Patient Portal FHIR service (/portalfhir/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Patient Portal FHIR REST API"

### Using FHIR API Internally

There are several ways to make API calls from an authorized session and maintain security:
Expand Down Expand Up @@ -574,6 +572,10 @@ Provenance resources are requested by including `_revinclude=Provenance:target`

## Patient Portal FHIR Endpoints

This is under development and is considered EXPERIMENTAL.

Enable the Patient Portal FHIR service (/portalfhir/ endpoints) in OpenEMR menu: Administration->Globals->Connectors->"Enable OpenEMR Patient Portal FHIR REST API (EXPERIMENTAL)"

OpenEMR patient portal fhir endpoints Use `http://localhost:8300/apis/default/portalfhir as base URI.`

Note that the `default` component can be changed to the name of the site when using OpenEMR's multisite feature.
Expand Down
2 changes: 1 addition & 1 deletion _rest_routes.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@
return $return;
},
"GET /api/appointment" => function () {
RestConfig::scope_check("user", "Appointment", "read");
RestConfig::scope_check("user", "appointment", "read");
RestConfig::authorization_check("patients", "appt");
$return = (new AppointmentRestController())->getAll();
RestConfig::apiLog($return);
Expand Down
10 changes: 9 additions & 1 deletion apis/dispatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -312,12 +312,20 @@
session_write_close();
}

// dispatch $routes called by ref.
// dispatch $routes called by ref (note storing the output in a variable to allow option
// to destroy the session/cookie before sending the output back)
ob_start();
$hasRoute = HttpRestRouteHandler::dispatch($routes, $resource, $_SERVER["REQUEST_METHOD"]);
$apiCallOutput = ob_get_clean();
// Tear down session for security.
if (!$isLocalApi) {
$gbl::destroySession();
}
// Send the output if not empty
if (!empty($apiCallOutput)) {
echo $apiCallOutput;
}

// prevent 200 if route doesn't exist
if (!$hasRoute) {
$logger->debug("dispatch.php no route found for resource", ['resource' => $resource]);
Expand Down
4 changes: 2 additions & 2 deletions library/globals.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -2928,14 +2928,14 @@ function gblTimeZones()
),

'rest_portal_api' => array(
xl('Enable OpenEMR Patient Portal REST API'),
xl('Enable OpenEMR Patient Portal REST API (EXPERIMENTAL)'),
'bool',
'0',
xl('Enable OpenEMR Patient Portal RESTful API.')
),

'rest_portal_fhir_api' => array(
xl('Enable OpenEMR Patient Portal FHIR REST API'),
xl('Enable OpenEMR Patient Portal FHIR REST API (EXPERIMENTAL)'),
'bool',
'0',
xl('Enable OpenEMR Patient Portal FHIR RESTful API.')
Expand Down
3 changes: 2 additions & 1 deletion oauth2/provider/.well-known/discovery.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
use OpenEMR\Common\Session\SessionUtil;

if ($oauthdisc !== true) {
echo xlt("Error. Not authorized");
$message = xlt("Error. Not authorized");
SessionUtil::oauthSessionCookieDestroy();
echo $message;
exit();
}

Expand Down
3 changes: 2 additions & 1 deletion oauth2/provider/jwk.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
use OpenEMR\Common\Session\SessionUtil;

if ($oauthjwk !== true) {
echo xlt("Error. Not authorized");
$message = xlt("Error. Not authorized");
SessionUtil::oauthSessionCookieDestroy();
echo $message;
exit();
}

Expand Down
7 changes: 5 additions & 2 deletions oauth2/provider/login.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
use OpenEMR\Common\Session\SessionUtil;

if ($oauthLogin !== true) {
echo xlt("Error. Not authorized");
$message = xlt("Error. Not authorized");
SessionUtil::oauthSessionCookieDestroy();
echo $message;
exit();
}

Expand Down Expand Up @@ -148,7 +149,9 @@
<div class="btn-group">
<?php if (empty($mfaRequired)) { ?>
<button type="submit" name="user_role" class="btn btn-outline-primary" value="api"><?php echo xlt("OpenEMR Login"); ?> <i class="fa fa-sign-in-alt"></i></button>
<button type="submit" name="user_role" class="btn btn-outline-info" value="portal-api"><?php echo xlt("Patient Login"); ?> <i class="fa fa-sign-in-alt"></i></button>
<?php if (!empty($patientRoleSupport)) { ?>
<button type="submit" name="user_role" class="btn btn-outline-info" value="portal-api"><?php echo xlt("Patient Login"); ?> <i class="fa fa-sign-in-alt"></i></button>
<?php } ?>
<?php } ?>
</div>
<div class="form-check-inline float-right">
Expand Down
86 changes: 45 additions & 41 deletions src/Common/Auth/OpenIDConnect/Repositories/ScopeRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -494,26 +494,28 @@ public function getCurrentSmartScopes(): array
}
}
$scopes_api_portal = [];
$restAPIs = $restHelper->getCapabilityRESTJSON($gbl::$PORTAL_FHIR_ROUTE_MAP);
foreach ($restAPIs as $resources) {
if (!empty($resources) && is_array($resources)) {
foreach ($resources as $resource) {
$interactions = $resource['interaction'];
$resourceType = $resource['type'];
foreach ($interactions as $interaction) {
$scopeRead = $resourceType . ".read";
$scopeWrite = $resourceType . ".write";
switch ($interaction['code']) {
case 'read':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'search-type':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'insert':
case 'update':
$scopes_api_portal['patient/' . $scopeWrite] = 'patient/' . $scopeWrite;
break;
if (!empty($GLOBALS['rest_portal_api']) || !empty($GLOBALS['rest_portal_fhir_api'])) {
$restAPIs = $restHelper->getCapabilityRESTJSON($gbl::$PORTAL_FHIR_ROUTE_MAP);
foreach ($restAPIs as $resources) {
if (!empty($resources) && is_array($resources)) {
foreach ($resources as $resource) {
$interactions = $resource['interaction'];
$resourceType = $resource['type'];
foreach ($interactions as $interaction) {
$scopeRead = $resourceType . ".read";
$scopeWrite = $resourceType . ".write";
switch ($interaction['code']) {
case 'read':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'search-type':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'insert':
case 'update':
$scopes_api_portal['patient/' . $scopeWrite] = 'patient/' . $scopeWrite;
break;
}
}
}
}
Expand Down Expand Up @@ -576,27 +578,29 @@ public function getCurrentStandardScopes(): array
}
}
$scopes_api_portal = [];
$restAPIs = $restHelper->getCapabilityRESTJSON($gbl::$PORTAL_ROUTE_MAP, "OpenEMR\\Services");
foreach ($restAPIs as $resources) {
if (!empty($resources) && is_array($resources)) {
foreach ($resources as $resource) {
$interactions = $resource['interaction'];
$resourceType = $resource['type'];
foreach ($interactions as $interaction) {
$scopeRead = $resourceType . ".read";
$scopeWrite = $resourceType . ".write";
switch ($interaction['code']) {
case 'read':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'search-type':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'put':
case 'insert':
case 'update':
$scopes_api_portal['patient/' . $scopeWrite] = 'patient/' . $scopeWrite;
break;
if (!empty($GLOBALS['rest_portal_api']) || !empty($GLOBALS['rest_portal_fhir_api'])) {
$restAPIs = $restHelper->getCapabilityRESTJSON($gbl::$PORTAL_ROUTE_MAP, "OpenEMR\\Services");
foreach ($restAPIs as $resources) {
if (!empty($resources) && is_array($resources)) {
foreach ($resources as $resource) {
$interactions = $resource['interaction'];
$resourceType = $resource['type'];
foreach ($interactions as $interaction) {
$scopeRead = $resourceType . ".read";
$scopeWrite = $resourceType . ".write";
switch ($interaction['code']) {
case 'read':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'search-type':
$scopes_api_portal['patient/' . $scopeRead] = 'patient/' . $scopeRead;
break;
case 'put':
case 'insert':
case 'update':
$scopes_api_portal['patient/' . $scopeWrite] = 'patient/' . $scopeWrite;
break;
}
}
}
}
Expand Down
10 changes: 6 additions & 4 deletions src/RestControllers/AuthorizationController.php
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ public function clientRegistration(): void
$body = $response->getBody();
$body->write(json_encode(array_merge($client_json, $params)));

$this->emitResponse($response->withStatus(200)->withBody($body));
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($response->withStatus(200)->withBody($body));
} catch (OAuthServerException $exception) {
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($exception->generateHttpResponse($response));
Expand Down Expand Up @@ -464,8 +464,8 @@ public function clientRegisteredDetails(): void
$body = $response->getBody();
$body->write(json_encode($params));

$this->emitResponse($response->withStatus(200)->withBody($body));
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($response->withStatus(200)->withBody($body));
} catch (OAuthServerException $exception) {
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($exception->generateHttpResponse($response));
Expand Down Expand Up @@ -649,6 +649,8 @@ public function userLogin(): void
{
$response = $this->createServerResponse();

$patientRoleSupport = (!empty($GLOBALS['rest_portal_api']) || !empty($GLOBALS['rest_portal_fhir_api']));

if (empty($_POST['username']) && empty($_POST['password'])) {
$this->logger->debug("AuthorizationController->userLogin() presenting blank login form");
$oauthLogin = true;
Expand Down Expand Up @@ -809,8 +811,8 @@ public function authorizeUser(): void
}
// Return the HTTP redirect response. Redirect is to client callback.
$this->logger->debug("AuthorizationController->authorizeUser() sending server response");
$this->emitResponse($result);
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($result);
exit;
} catch (Exception $exception) {
$this->logger->error("AuthorizationController->authorizeUser() Exception thrown", ["message" => $exception->getMessage()]);
Expand Down Expand Up @@ -891,7 +893,6 @@ public function oauthAuthorizeToken(): void
throw new OAuthServerException('Bad request', 0, 'invalid_request', 400);
}
$result = $server->respondToAccessTokenRequest($request, $response);
$this->emitResponse($result);
// save a password trusted user
if ($this->grantType === 'password') {
$body = $result->getBody();
Expand All @@ -903,6 +904,7 @@ public function oauthAuthorizeToken(): void
$this->saveTrustedUser($_REQUEST['client_id'], $_SESSION['pass_user_id'], $_REQUEST['scope'], 0, $code, $session_cache, 'password');
}
SessionUtil::oauthSessionCookieDestroy();
$this->emitResponse($result);
} catch (OAuthServerException $exception) {
$this->logger->debug(
"AuthorizationController->oauthAuthorizeToken() OAuthServerException occurred",
Expand Down

0 comments on commit fd32973

Please sign in to comment.