# HG changeset patch # User Alain Mazy # Date 1718375213 -7200 # Node ID 85859ec3aa7e536e50e458a3a5fd05a31031bb5d # Parent c4b908970ae4e4f62385c9cf49e79b871442b7e4 added support for roles/permissions edition diff -r c4b908970ae4 -r 85859ec3aa7e NEWS --- a/NEWS Thu May 30 21:59:01 2024 +0200 +++ b/NEWS Fri Jun 14 16:26:53 2024 +0200 @@ -1,3 +1,13 @@ +Pending changes in the mainline +=============================== + +* Added support for roles/permissions edition: + - new configuration "WebServiceSettingsRolesUrl" + - new API routes: + - /auth/settings/roles (GET/PUT) + - /auth/settings/permissions (GET) + + 2024-05-16 - v 0.7.2 ==================== diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/AuthorizationWebService.cpp --- a/Plugin/AuthorizationWebService.cpp Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/AuthorizationWebService.cpp Fri Jun 14 16:26:53 2024 +0200 @@ -344,6 +344,7 @@ throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "Syntax error in the result of the Auth Web service, the format of the UserProfile is invalid"); } + // LOG(INFO) << jsonProfile.toStyledString(); profile.name = jsonProfile[USER_NAME].asString(); @@ -451,4 +452,76 @@ return false; } + bool AuthorizationWebService::GetSettingsRoles(Json::Value& roles) + { + if (settingsRolesUrl_.empty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Can not get settings-roles if the 'WebServiceSettingsRolesUrl' is not configured"); + } + + Orthanc::WebServiceParameters authWebservice; + + if (!username_.empty()) + { + authWebservice.SetCredentials(username_, password_); + } + + try + { + Orthanc::HttpClient authClient(authWebservice, ""); + authClient.SetUrl(settingsRolesUrl_); + authClient.SetMethod(Orthanc::HttpMethod_Get); + authClient.AddHeader("Expect", ""); + authClient.SetTimeout(10); + + authClient.ApplyAndThrowException(roles); + + return true; + } + catch (Orthanc::OrthancException& ex) + { + return false; + } + + } + + bool AuthorizationWebService::UpdateSettingsRoles(Json::Value& response, const Json::Value& roles) + { + if (settingsRolesUrl_.empty()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadRequest, "Can not update settings-roles if the 'WebServiceSettingsRolesUrl' is not configured"); + } + + Orthanc::WebServiceParameters authWebservice; + + if (!username_.empty()) + { + authWebservice.SetCredentials(username_, password_); + } + + try + { + std::string bodyAsString; + Orthanc::Toolbox::WriteFastJson(bodyAsString, roles); + + Orthanc::HttpClient authClient(authWebservice, ""); + authClient.SetUrl(settingsRolesUrl_); + authClient.AssignBody(bodyAsString); + authClient.SetMethod(Orthanc::HttpMethod_Put); + authClient.AddHeader("Content-Type", "application/json"); + authClient.AddHeader("Expect", ""); + authClient.SetTimeout(10); + + authClient.ApplyAndThrowException(response); + + return true; + } + catch (Orthanc::OrthancException& ex) + { + return false; + } + + } + + } diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/AuthorizationWebService.h --- a/Plugin/AuthorizationWebService.h Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/AuthorizationWebService.h Fri Jun 14 16:26:53 2024 +0200 @@ -35,6 +35,7 @@ std::string tokenValidationUrl_; std::string tokenDecoderUrl_; std::string tokenCreationBaseUrl_; + std::string settingsRolesUrl_; protected: virtual bool IsGrantedInternal(unsigned int& validity, @@ -56,11 +57,13 @@ AuthorizationWebService(const std::string& tokenValidationUrl, const std::string& tokenCreationBaseUrl, const std::string& userProfileUrl, - const std::string& tokenDecoderUrl) : + const std::string& tokenDecoderUrl, + const std::string& settingsRolesUrl) : userProfileUrl_(userProfileUrl), tokenValidationUrl_(tokenValidationUrl), tokenDecoderUrl_(tokenDecoderUrl), - tokenCreationBaseUrl_(tokenCreationBaseUrl) + tokenCreationBaseUrl_(tokenCreationBaseUrl), + settingsRolesUrl_(settingsRolesUrl) { } @@ -84,6 +87,11 @@ return !tokenValidationUrl_.empty(); } + virtual bool HasSettingsRoles() const + { + return !settingsRolesUrl_.empty(); + } + virtual bool CreateToken(IAuthorizationService::CreatedToken& response, const std::string& tokenType, const std::string& id, @@ -95,6 +103,10 @@ const std::string& tokenKey, const std::string& tokenValue); + virtual bool GetSettingsRoles(Json::Value& roles); + virtual bool UpdateSettingsRoles(Json::Value& response, + const Json::Value& roles); + static void ToJson(Json::Value& output, const UserProfile& profile); static void FromJson(UserProfile& profile, const Json::Value& input); diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/CachedAuthorizationService.h --- a/Plugin/CachedAuthorizationService.h Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/CachedAuthorizationService.h Fri Jun 14 16:26:53 2024 +0200 @@ -106,5 +106,15 @@ tokenValue); } + virtual bool GetSettingsRoles(Json::Value& roles) + { + return decorated_->GetSettingsRoles(roles); + } + + virtual bool UpdateSettingsRoles(Json::Value& response, const Json::Value& roles) + { + return decorated_->UpdateSettingsRoles(response, roles); + } + }; } diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/DefaultConfiguration.json --- a/Plugin/DefaultConfiguration.json Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/DefaultConfiguration.json Fri Jun 14 16:26:53 2024 +0200 @@ -115,7 +115,13 @@ // settings ["put", "^/tools/log-level$", "all|settings"], - ["get", "^/tools/log-level$", "all|settings"] + ["get", "^/tools/log-level$", "all|settings"], + + // permission settings + ["put", "^/auth/settings/roles$", "admin-permissions"], + ["get", "^/auth/settings/roles$", "admin-permissions"], + ["get", "^/auth/settings/permissions$", "admin-permissions"] + ] } } \ No newline at end of file diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/IAuthorizationService.h --- a/Plugin/IAuthorizationService.h Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/IAuthorizationService.h Fri Jun 14 16:26:53 2024 +0200 @@ -110,5 +110,10 @@ virtual bool HasUserProfile() const = 0; virtual bool HasCreateToken() const = 0; virtual bool HasTokenValidation() const = 0; + + virtual bool GetSettingsRoles(Json::Value& roles) = 0; + virtual bool UpdateSettingsRoles(Json::Value& response, + const Json::Value& roles) = 0; + }; } diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/PermissionParser.cpp --- a/Plugin/PermissionParser.cpp Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/PermissionParser.cpp Fri Jun 14 16:26:53 2024 +0200 @@ -151,6 +151,20 @@ LOG(WARNING) << "Authorization plugin: adding a new permission pattern: " << lowerCaseMethod << " " << regex << " - " << permission; permissionsPattern_.push_back(PermissionPattern(parsedMethod, regex, permission)); + + { // extract individual permissions + std::set permissions; + Orthanc::Toolbox::SplitString(permissions, permission, '|'); + + for (std::set::const_iterator it = permissions.begin(); it != permissions.end(); ++it) + { + if (!it->empty()) + { + permissionsList_.insert(*it); + } + } + + } } bool PermissionParser::Parse(std::set& permissions, diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/PermissionParser.h --- a/Plugin/PermissionParser.h Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/PermissionParser.h Fri Jun 14 16:26:53 2024 +0200 @@ -41,6 +41,7 @@ private: mutable boost::mutex mutex_; std::list permissionsPattern_; + std::set permissionsList_; std::string dicomWebRoot_; std::string oe2Root_; @@ -58,5 +59,10 @@ std::string& matchedPattern, const OrthancPluginHttpMethod& method, const std::string& uri) const; + + const std::set& GetPermissionsList() const + { + return permissionsList_; + } }; } diff -r c4b908970ae4 -r 85859ec3aa7e Plugin/Plugin.cpp --- a/Plugin/Plugin.cpp Thu May 30 21:59:01 2024 +0200 +++ b/Plugin/Plugin.cpp Fri Jun 14 16:26:53 2024 +0200 @@ -1054,6 +1054,75 @@ } } + +void AuthSettingsRoles(OrthancPluginRestOutput* output, + const char* /*url*/, + const OrthancPluginHttpRequest* request) +{ + OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); + + if (authorizationService_.get() == NULL) // this is not suppposed to happen + { + OrthancPlugins::AnswerHttpError(404, output); + return; + } + + if (request->method == OrthancPluginHttpMethod_Get) + { + Json::Value roles; + + if (!authorizationService_->GetSettingsRoles(roles)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Could not retrieve roles from the auth-service", true); + } + + OrthancPlugins::AnswerJson(roles, output); + } + else if (request->method == OrthancPluginHttpMethod_Put) + { + Json::Value roles; + Json::Value response; + + if (!OrthancPlugins::ReadJson(roles, request->body, request->bodySize)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "A JSON payload was expected"); + } + + if (!authorizationService_->UpdateSettingsRoles(response, roles)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError, "Could not update roles in the auth-service", true); + } + OrthancPlugins::AnswerJson(response, output); + } + else + { + OrthancPluginSendMethodNotAllowed(context, output, "GET,PUT"); + } +} + + +void GetPermissionList(OrthancPluginRestOutput* output, + const char* /*url*/, + const OrthancPluginHttpRequest* request) +{ + OrthancPluginContext* context = OrthancPlugins::GetGlobalContext(); + + if (request->method != OrthancPluginHttpMethod_Get) + { + OrthancPluginSendMethodNotAllowed(context, output, "GET"); + } + else + { + std::set permissionsList = permissionParser_->GetPermissionsList(); + + Json::Value response = Json::arrayValue; + Orthanc::SerializationToolbox::WriteSetOfStrings(response, permissionsList); + + OrthancPlugins::AnswerJson(response, output); + } +} + + void MergeJson(Json::Value &a, const Json::Value &b) { if (!a.isObject() || !b.isObject()) @@ -1187,6 +1256,7 @@ std::string urlTokenValidation; std::string urlTokenCreationBase; std::string urlUserProfile; + std::string urlSettingsRole; std::string urlRoot; static const char* WEB_SERVICE_ROOT = "WebServiceRootUrl"; @@ -1194,6 +1264,7 @@ static const char* WEB_SERVICE_TOKEN_VALIDATION = "WebServiceTokenValidationUrl"; static const char* WEB_SERVICE_TOKEN_CREATION_BASE = "WebServiceTokenCreationBaseUrl"; static const char* WEB_SERVICE_USER_PROFILE = "WebServiceUserProfileUrl"; + static const char* WEB_SERVICE_SETTINGS_ROLES = "WebServiceSettingsRolesUrl"; static const char* WEB_SERVICE_TOKEN_VALIDATION_LEGACY = "WebService"; if (pluginConfiguration.LookupStringValue(urlRoot, WEB_SERVICE_ROOT)) { @@ -1201,6 +1272,7 @@ urlTokenValidation = Orthanc::Toolbox::JoinUri(urlRoot, "/tokens/validate"); urlTokenCreationBase = Orthanc::Toolbox::JoinUri(urlRoot, "/tokens/"); urlUserProfile = Orthanc::Toolbox::JoinUri(urlRoot, "/user/get-profile"); + urlSettingsRole = Orthanc::Toolbox::JoinUri(urlRoot, "/settings/roles"); } else { @@ -1213,6 +1285,7 @@ pluginConfiguration.LookupStringValue(urlTokenCreationBase, WEB_SERVICE_TOKEN_CREATION_BASE); pluginConfiguration.LookupStringValue(urlUserProfile, WEB_SERVICE_USER_PROFILE); + pluginConfiguration.LookupStringValue(urlSettingsRole, WEB_SERVICE_SETTINGS_ROLES); } authorizationParser_.reset(new OrthancPlugins::DefaultAuthorizationParser(factory, dicomWebRoot)); @@ -1259,6 +1332,15 @@ LOG(WARNING) << "Authorization plugin: no base url defined for Token Creation"; } + if (!urlSettingsRole.empty()) + { + LOG(WARNING) << "Authorization plugin: settings-roles url defined : " << urlSettingsRole; + } + else + { + LOG(WARNING) << "Authorization plugin: no settings-roles url defined"; + } + if (!resourceTokensEnabled_ && permissionParser_.get() == NULL) { if (hasBasicAuthEnabled) @@ -1367,7 +1449,8 @@ std::unique_ptr webService(new OrthancPlugins::AuthorizationWebService(urlTokenValidation, urlTokenCreationBase, urlUserProfile, - urlTokenDecoder)); + urlTokenDecoder, + urlSettingsRole)); std::string webServiceIdentifier; if (pluginConfiguration.LookupStringValue(webServiceIdentifier, "WebServiceIdentifier")) @@ -1401,6 +1484,8 @@ OrthancPlugins::RegisterRestCallback("/auth/user/profile", true); OrthancPlugins::RegisterRestCallback("/tools/find", true); OrthancPlugins::RegisterRestCallback("/tools/labels", true); + OrthancPlugins::RegisterRestCallback("/auth/settings/roles", true); + OrthancPlugins::RegisterRestCallback("/auth/settings/permissions", true); } if (!urlTokenCreationBase.empty())