Mercurial > hg > orthanc
changeset 4343:e1e918e790e8
New function in the SDK: OrthancPluginGenerateRestApiAuthorizationToken()
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Fri, 04 Dec 2020 18:28:23 +0100 |
parents | 52166629239f |
children | 25112063257b |
files | NEWS OrthancFramework/Sources/HttpServer/HttpServer.cpp OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h OrthancServer/Plugins/Engine/OrthancPlugins.cpp OrthancServer/Plugins/Engine/OrthancPlugins.h OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h OrthancServer/Sources/main.cpp |
diffstat | 7 files changed, 147 insertions(+), 24 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS Thu Dec 03 18:48:06 2020 +0100 +++ b/NEWS Fri Dec 04 18:28:23 2020 +0100 @@ -21,6 +21,11 @@ body to complement C-GET SCU with C-FIND SCU ("DicomEchoChecksFind" on a per-connection basis) * Archive/media jobs report the size of the created ZIP file in content field "ArchiveSizeMB" +Plugins +------- + +* New function in the SDK: OrthancPluginGenerateRestApiAuthorizationToken() + Maintenance ----------- @@ -36,9 +41,17 @@ Version 1.8.0 (2020-10-16) ========================== +General +------- + * Serving the content of Orthanc as a WebDAV network share * New config options: "WebDavEnabled", "WebDavDeleteAllowed" and "WebDavUploadAllowed" +Plugins +------- + +* New available origin for a DICOM instance: "OrthancPluginInstanceOrigin_WebDav" + Version 1.7.4 (2020-09-18) ==========================
--- a/OrthancFramework/Sources/HttpServer/HttpServer.cpp Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancFramework/Sources/HttpServer/HttpServer.cpp Fri Dec 04 18:28:23 2020 +0100 @@ -545,24 +545,45 @@ } - static bool IsAccessGranted(const HttpServer& that, - const HttpToolbox::Arguments& headers) + enum AccessMode { - bool granted = false; + AccessMode_Forbidden, + AccessMode_AuthorizationToken, + AccessMode_RegisteredUser + }; + + + static AccessMode IsAccessGranted(const HttpServer& that, + const HttpToolbox::Arguments& headers) + { + static const std::string BASIC = "Basic "; + static const std::string BEARER = "Bearer "; HttpToolbox::Arguments::const_iterator auth = headers.find("authorization"); if (auth != headers.end()) { std::string s = auth->second; - if (s.size() > 6 && - s.substr(0, 6) == "Basic ") + if (boost::starts_with(s, BASIC)) { - std::string b64 = s.substr(6); - granted = that.IsValidBasicHttpAuthentication(b64); + std::string b64 = s.substr(BASIC.length()); + if (that.IsValidBasicHttpAuthentication(b64)) + { + return AccessMode_RegisteredUser; + } + } + else if (boost::starts_with(s, BEARER) && + that.GetIncomingHttpRequestFilter() != NULL) + { + // New in Orthanc 1.8.1 + std::string token = s.substr(BEARER.length()); + if (that.GetIncomingHttpRequestFilter()->IsValidBearerToken(token)) + { + return AccessMode_AuthorizationToken; + } } } - return granted; + return AccessMode_Forbidden; } @@ -1075,7 +1096,7 @@ if (!server.IsRemoteAccessAllowed() && !localhost) { - output.SendUnauthorized(server.GetRealm()); + output.SendUnauthorized(server.GetRealm()); // 401 error return; } @@ -1105,12 +1126,14 @@ HttpToolbox::ParseGetArguments(argumentsGET, request->query_string); } + + AccessMode accessMode = IsAccessGranted(server, headers); // Authenticate this connection if (server.IsAuthenticationEnabled() && - !IsAccessGranted(server, headers)) + accessMode == AccessMode_Forbidden) { - output.SendUnauthorized(server.GetRealm()); + output.SendUnauthorized(server.GetRealm()); // 401 error return; } @@ -1195,16 +1218,22 @@ } - // Check that this connection is allowed by the user's authentication filter const std::string username = GetAuthenticatedUsername(headers); - IIncomingHttpRequestFilter *filter = server.GetIncomingHttpRequestFilter(); - if (filter != NULL) + if (accessMode != AccessMode_AuthorizationToken) { - if (!filter->IsAllowed(filterMethod, requestUri, remoteIp, + // Check that this access is granted by the user's authorization + // filter. In the case of an authorization bearer token, grant + // full access to the API. + + assert(accessMode == AccessMode_Forbidden || // Could be the case if "!server.IsAuthenticationEnabled()" + accessMode == AccessMode_RegisteredUser); + + IIncomingHttpRequestFilter *filter = server.GetIncomingHttpRequestFilter(); + if (filter != NULL && + !filter->IsAllowed(filterMethod, requestUri, remoteIp, username.c_str(), headers, argumentsGET)) { - //output.SendUnauthorized(server.GetRealm()); output.SendStatus(HttpStatus_403_Forbidden); return; }
--- a/OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancFramework/Sources/HttpServer/IIncomingHttpRequestFilter.h Fri Dec 04 18:28:23 2020 +0100 @@ -33,6 +33,9 @@ { } + // New in Orthanc 1.8.1 + virtual bool IsValidBearerToken(const std::string& token) = 0; + virtual bool IsAllowed(HttpMethod method, const char* uri, const char* ip,
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.cpp Fri Dec 04 18:28:23 2020 +0100 @@ -918,6 +918,7 @@ RefreshMetricsCallbacks refreshMetricsCallbacks_; StorageCommitmentScpCallbacks storageCommitmentScpCallbacks_; std::unique_ptr<StorageAreaFactory> storageArea_; + std::set<std::string> authorizationTokens_; boost::recursive_mutex restCallbackMutex_; boost::recursive_mutex storedCallbackMutex_; @@ -4647,6 +4648,18 @@ return true; } + case _OrthancPluginService_GenerateRestApiAuthorizationToken: + { + const _OrthancPluginRetrieveDynamicString& p = + *reinterpret_cast<const _OrthancPluginRetrieveDynamicString*>(parameters); + const std::string token = Toolbox::GenerateUuid(); + + pimpl_->authorizationTokens_.insert(token); + *p.result = CopyString("Bearer " + token); + + return true; + } + default: { // This service is unknown to the Orthanc plugin engine @@ -5257,4 +5270,11 @@ return false; } + + + bool OrthancPlugins::IsValidAuthorizationToken(const std::string& token) const + { + boost::recursive_mutex::scoped_lock lock(pimpl_->invokeServiceMutex_); + return (pimpl_->authorizationTokens_.find(token) != pimpl_->authorizationTokens_.end()); + } }
--- a/OrthancServer/Plugins/Engine/OrthancPlugins.h Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancServer/Plugins/Engine/OrthancPlugins.h Fri Dec 04 18:28:23 2020 +0100 @@ -80,7 +80,6 @@ public IServerListener, public IWorklistRequestHandlerFactory, public IDicomImageDecoder, - public IIncomingHttpRequestFilter, public IFindRequestHandlerFactory, public IMoveRequestHandlerFactory, public IStorageCommitmentFactory, @@ -348,12 +347,12 @@ size_t size, unsigned int frame) ORTHANC_OVERRIDE; - virtual bool IsAllowed(HttpMethod method, - const char* uri, - const char* ip, - const char* username, - const HttpToolbox::Arguments& httpHeaders, - const HttpToolbox::GetArguments& getArguments) ORTHANC_OVERRIDE; + bool IsAllowed(HttpMethod method, + const char* uri, + const char* ip, + const char* username, + const HttpToolbox::Arguments& httpHeaders, + const HttpToolbox::GetArguments& getArguments); bool HasFindHandler(); @@ -385,6 +384,9 @@ const std::vector<std::string>& sopInstanceUids, const std::string& remoteAet, const std::string& calledAet) ORTHANC_OVERRIDE; + + // New in Orthanc 1.8.1 (cf. "OrthancPluginGenerateRestApiAuthorizationToken()") + bool IsValidAuthorizationToken(const std::string& token) const; }; }
--- a/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancServer/Plugins/Include/orthanc/OrthancCPlugin.h Fri Dec 04 18:28:23 2020 +0100 @@ -117,7 +117,7 @@ #define ORTHANC_PLUGINS_MINIMAL_MAJOR_NUMBER 1 #define ORTHANC_PLUGINS_MINIMAL_MINOR_NUMBER 8 -#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 0 +#define ORTHANC_PLUGINS_MINIMAL_REVISION_NUMBER 1 #if !defined(ORTHANC_PLUGINS_VERSION_IS_ABOVE) @@ -435,6 +435,7 @@ _OrthancPluginService_EncodeDicomWebJson2 = 36, /* New in Orthanc 1.7.0 */ _OrthancPluginService_EncodeDicomWebXml2 = 37, /* New in Orthanc 1.7.0 */ _OrthancPluginService_CreateMemoryBuffer = 38, /* New in Orthanc 1.7.0 */ + _OrthancPluginService_GenerateRestApiAuthorizationToken = 39, /* New in Orthanc 1.8.1 */ /* Registration of callbacks */ _OrthancPluginService_RegisterRestCallback = 1000, @@ -8197,6 +8198,51 @@ } + /** + * @brief Generate a token to grant full access to the REST API of Orthanc + * + * This function generates a token that can be set in the HTTP + * header "Authorization" so as to grant full access to the REST API + * of Orthanc using an external HTTP client. Using this function + * avoids the need of adding a separate user in the + * "RegisteredUsers" configuration of Orthanc, which eases + * deployments. + * + * This feature is notably useful in multiprocess scenarios, where a + * subprocess created by a plugin has no access to the + * "OrthancPluginContext", and thus cannot call + * "OrthancPluginRestApi[Get|Post|Put|Delete]()". + * + * This situation is frequently encountered in Python plugins, where + * the "multiprocessing" package can be used to bypass the Global + * Interpreter Lock (GIL) and thus to improve performance and + * concurrency. + * + * @param context The Orthanc plugin context, as received by OrthancPluginInitialize(). + * @return The authorization token, or NULL value in the case of an error. + * This string must be freed by OrthancPluginFreeString(). + * @ingroup Orthanc + **/ + ORTHANC_PLUGIN_INLINE char* OrthancPluginGenerateRestApiAuthorizationToken( + OrthancPluginContext* context) + { + char* result; + + _OrthancPluginRetrieveDynamicString params; + params.result = &result; + params.argument = NULL; + + if (context->InvokeService(context, _OrthancPluginService_GenerateRestApiAuthorizationToken, + ¶ms) != OrthancPluginErrorCode_Success) + { + /* Error */ + return NULL; + } + else + { + return result; + } + } #ifdef __cplusplus } #endif
--- a/OrthancServer/Sources/main.cpp Thu Dec 03 18:48:06 2020 +0100 +++ b/OrthancServer/Sources/main.cpp Fri Dec 04 18:28:23 2020 +0100 @@ -465,6 +465,16 @@ { } + virtual bool IsValidBearerToken(const std::string& token) ORTHANC_OVERRIDE + { +#if ORTHANC_ENABLE_PLUGINS == 1 + return (plugins_ != NULL && + plugins_->IsValidAuthorizationToken(token)); +#else + return false; +#endif + } + virtual bool IsAllowed(HttpMethod method, const char* uri, const char* ip,