Mercurial > hg > orthanc-education
view Sources/EducationRestApi.cpp @ 1:8a5a6c4efdd7
fix compatibility with older compilers
| author | Sebastien Jodogne <s.jodogne@gmail.com> |
|---|---|
| date | Wed, 08 Oct 2025 23:16:17 +0200 |
| parents | 86c6ac51044a |
| children | efab855a0888 |
line wrap: on
line source
/** * SPDX-FileCopyrightText: 2024-2025 Sebastien Jodogne, ICTEAM UCLouvain, Belgium * SPDX-License-Identifier: AGPL-3.0-or-later */ /** * Orthanc for Education * Copyright (C) 2024-2025 Sebastien Jodogne, EPL UCLouvain, Belgium * * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ #include "EducationRestApi.h" #include "EducationConfiguration.h" #include "LTI/LTIRoutes.h" #include "OrthancDatabase.h" #include "ProjectPermissionContext.h" #include "RestApiRouter.h" #include <MultiThreading/Semaphore.h> #include <SerializationToolbox.h> #include <SystemToolbox.h> #include <boost/algorithm/string/predicate.hpp> #include <cassert> static const char* const COOKIE_USER_AUTH = "orthanc-education-user"; void ServeWebApplication(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { assert(user.GetRole() == Role_Guest); static const std::string PREFIX = "/education/app/"; if (!boost::starts_with(url, PREFIX)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } const std::string filename = std::string(url).substr(PREFIX.length()); const Orthanc::MimeType mime = Orthanc::SystemToolbox::AutodetectMimeType(filename); std::string content; HttpToolbox::GetWebApplicationResource(content, filename); OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, content.c_str(), content.size(), Orthanc::EnumerationToString(mime)); } void DoLogin(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& oldUser, const Json::Value& body) { assert(oldUser.GetRole() == Role_Guest); const std::string username = Orthanc::SerializationToolbox::ReadString(body, "username"); const std::string password = Orthanc::SerializationToolbox::ReadString(body, "password"); std::unique_ptr<AuthenticatedUser> user(EducationConfiguration::GetInstance().DoLoginAuthentication(username, password)); if (user.get() == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_Unauthorized); } else { std::string token; user->ForgeJWT(token, EducationConfiguration::GetInstance().GetLtiContext(), EducationConfiguration::GetInstance().GetMaxLoginAgeSeconds()); EducationConfiguration::GetInstance().GetLtiContext().CloseSession(output); HttpToolbox::SetCookie(output, COOKIE_USER_AUTH, token, CookieSameSite_Lax); Json::Value answer; HttpToolbox::AnswerJson(output, answer); } } void DoLogout(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { assert(user.GetRole() == Role_Guest); EducationConfiguration::GetInstance().GetLtiContext().CloseSession(output); HttpToolbox::ClearCookie(output, COOKIE_USER_AUTH, CookieSameSite_Lax); ClearLTICookie(output); // We manually reimplement "OrthancPluginRedirect()", otherwise "Set-Cookie" has no effect OrthancPluginSetHttpHeader(OrthancPlugins::GetGlobalContext(), output, "Location", ".." /* redirect to the root */); OrthancPluginSendHttpStatusCode(OrthancPlugins::GetGlobalContext(), output, 302); } void GenerateListProjectUrl(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Guest); const std::string projectId = Orthanc::SerializationToolbox::ReadString(body, "project"); std::string s; Orthanc::Toolbox::UriEncode(s, projectId); const std::string listUrl = "education/app/list-projects.html?open-project-id=" + s; Json::Value answer; answer["relative_url"] = listUrl; std::string absolute; if (EducationConfiguration::GetInstance().GetAbsoluteUrl(absolute, listUrl)) { answer["absolute_url"] = absolute; } HttpToolbox::AnswerJson(output, answer); } void GenerateViewerUrlFromResource(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Guest); const ViewerType viewer = ParseViewerType(Orthanc::SerializationToolbox::ReadString(body, "viewer")); Json::Value resource; if (!HttpToolbox::LookupJsonObject(resource, body, "resource")) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } const std::string viewerUrl = OrthancDatabase::GenerateViewerUrl(viewer, resource); Json::Value answer; answer["relative_url"] = viewerUrl; std::string absolute; if (EducationConfiguration::GetInstance().GetAbsoluteUrl(absolute, viewerUrl)) { answer["absolute_url"] = absolute; } HttpToolbox::AnswerJson(output, answer); } void GetUserProjects(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { if (user.GetRole() != Role_Administrator && user.GetRole() != Role_Standard) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess); } // List all the projects Json::Value projects = Json::objectValue; { DocumentOrientedDatabase::Iterator iterator(ProjectPermissionContext::GetProjects()); while (iterator.Next()) { const Project& project = iterator.GetDocument<Project>(); const ProjectAccessMode mode = ProjectPermissionContext::GetProjectAccessMode(user, iterator.GetKey(), project); Json::Value item; if (mode == ProjectAccessMode_Writable) { item["role"] = "instructor"; } else if (EducationConfiguration::GetInstance().IsListProjectsAsLearner() && mode == ProjectAccessMode_ReadOnly) { item["role"] = "learner"; } else { continue; } OrthancDatabase::FormatProjectWithResources(item, iterator.GetKey(), project); projects[iterator.GetKey()] = item; } } Json::Value answer = Json::objectValue; user.Serialize(answer["user"]); answer["projects"] = projects; HttpToolbox::AnswerJson(output, answer); } static const char* const GetHomepage(Role role) { switch (role) { case Role_Administrator: // Redirect to the dashboard if the user is already logged as an administrator return "education/app/dashboard.html"; case Role_Standard: return "education/app/list-projects.html"; case Role_Guest: // By default, redirect to the login page return "education/app/login.html"; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); } } void RedirectRoot(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { const std::string homepage = GetHomepage(user.GetRole()); OrthancPluginRedirect(OrthancPlugins::GetGlobalContext(), output, homepage.c_str()); } void ServeConfiguration(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { Json::Value config; user.Serialize(config["user"]); if (user.GetRole() == Role_Administrator) { // This information is available to "dashboard.html", but not to "list-projects.html" config["has_orthanc_explorer_2"] = EducationConfiguration::GetInstance().HasPluginOrthancExplorer2(); config["lti_client_id"] = EducationConfiguration::GetInstance().GetLtiClientId(); config["lti_platform_url"] = EducationConfiguration::GetInstance().GetLtiPlatformUrl(); config["lti_platform_keys_url"] = EducationConfiguration::GetInstance().GetLtiPlatformKeysUrl(); config["lti_platform_redirection_url"] = EducationConfiguration::GetInstance().GetLtiPlatformRedirectionUrl(); } std::set<ViewerType> viewers; EducationConfiguration::GetInstance().ListAvailableViewers(viewers); HttpToolbox::FormatViewers(config["viewers"], viewers); if (viewers.empty() || viewers.find(ViewerType_StoneWebViewer) != viewers.end()) { config["default_viewer"] = EnumerationToString(ViewerType_StoneWebViewer); } else { config["default_viewer"] = EnumerationToString(*viewers.begin()); } config["label_prefix"] = LABEL_PREFIX; HttpToolbox::AnswerJson(output, config); } void ServeLogin(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { if (user.GetRole() == Role_Administrator || user.GetRole() == Role_Standard) { // Special case: If the user is already logged in, redirect to the homepage std::string url = Orthanc::Toolbox::JoinUri("../..", GetHomepage(user.GetRole())); OrthancPluginRedirect(OrthancPlugins::GetGlobalContext(), output, url.c_str()); } else { std::string content; HttpToolbox::GetWebApplicationResource(content, "login.html"); OrthancPluginAnswerBuffer(OrthancPlugins::GetGlobalContext(), output, content.c_str(), content.size(), Orthanc::EnumerationToString(Orthanc::MimeType_Html)); } } static std::string GetJsonString(const Json::Value& json) { if (json.type() == Json::stringValue) { return json.asString(); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } } void ChangeProjectParameter(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { const std::string key(request->groups[0]); const std::string property(request->groups[1]); std::unique_ptr<Project> project(ProjectPermissionContext::GetProjects().CloneDocument<Project>(key)); if (ProjectPermissionContext::GetProjectAccessMode(user, key, *project) != ProjectAccessMode_Writable) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess); } if (user.GetRole() != Role_Administrator && property != "policy" && property != "primary-viewer" && property != "secondary-viewer") { // Other properties can only be changed by the administrator throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess); } if (request->method == OrthancPluginHttpMethod_Put) { Json::Value body; if (!Orthanc::Toolbox::ReadJson(body, request->body, request->bodySize)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } else { if (property == "name") { project->SetName(GetJsonString(body)); } else if (property == "description") { project->SetDescription(GetJsonString(body)); } else if (property == "policy") { project->SetPolicy(ParseProjectPolicy(GetJsonString(body))); } else if (property == "primary-viewer") { project->SetPrimaryViewer(ParseViewerType(GetJsonString(body))); } else if (property == "secondary-viewers") { std::set<std::string> items; Orthanc::SerializationToolbox::ReadSetOfStrings(items, body); std::set<ViewerType> viewers; for (std::set<std::string>::const_iterator it = items.begin(); it != items.end(); ++it) { viewers.insert(ParseViewerType(*it)); } project->SetSecondaryViewers(viewers); } else if (property == "instructors") { std::set<std::string> items; Orthanc::SerializationToolbox::ReadSetOfStrings(items, body); project->SetInstructors(items); } else if (property == "learners") { std::set<std::string> items; Orthanc::SerializationToolbox::ReadSetOfStrings(items, body); project->SetLearners(items); } else if (property == "lti-context-id") { project->SetLtiContextId(GetJsonString(body)); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } } else if (request->method == OrthancPluginHttpMethod_Delete) { if (property == "lti-context-id") { project->ClearLtiContextId(); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } else { OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "PUT,DELETE"); return; } ProjectPermissionContext::GetProjects().Store(key, project.release()); HttpToolbox::AnswerText(output, ""); } static Orthanc::Semaphore previewThrottler_(8); template <Orthanc::ResourceType level> void GeneratePreview(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { const std::string resourceId(request->groups[0]); { std::unique_ptr<IPermissionContext> context(EducationConfiguration::GetInstance().CreatePermissionContext()); if (!OrthancDatabase::IsGrantedResource(*context, user, level, resourceId)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_ForbiddenAccess); } } std::string resourcePath; std::string lastUpdatePath; switch (level) { case Orthanc::ResourceType_Study: resourcePath = "/studies/" + resourceId; lastUpdatePath = resourcePath + "/metadata/LastUpdate"; break; case Orthanc::ResourceType_Series: resourcePath = "/series/" + resourceId; lastUpdatePath = resourcePath + "/metadata/LastUpdate"; break; case Orthanc::ResourceType_Instance: resourcePath = "/instances/" + resourceId; lastUpdatePath = resourcePath + "/metadata/ReceptionDate"; break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } std::string preview; std::string lastUpdate; if (OrthancPlugins::RestApiGetString(lastUpdate, lastUpdatePath, false)) { Json::Value metadata; if (OrthancPlugins::RestApiGet(metadata, resourcePath + "/metadata/" + METADATA_PREVIEW, false)) { if (lastUpdate == Orthanc::SerializationToolbox::ReadString(metadata, "last-update", "")) { std::string b64 = Orthanc::SerializationToolbox::ReadString(metadata, "jpeg", ""); if (!b64.empty()) { Orthanc::Toolbox::DecodeBase64(preview, b64); HttpToolbox::AnswerBuffer(output, preview, Orthanc::MimeType_Jpeg); return; } } } } { Orthanc::Semaphore::Locker locker(previewThrottler_); OrthancPlugins::HttpHeaders headers; headers["Accept"] = Orthanc::EnumerationToString(Orthanc::MimeType_Jpeg); bool success = false; switch (level) { case Orthanc::ResourceType_Study: { Json::Value study, series; if (OrthancPlugins::RestApiGet(study, resourcePath, false) && OrthancPlugins::RestApiGet(series, "/series/" + study["Series"][0].asString(), false)) { const std::string instance = series["Instances"][0].asString(); success = OrthancPlugins::RestApiGetString(preview, "/instances/" + instance + "/preview", headers, false); } break; } case Orthanc::ResourceType_Series: { Json::Value series; if (OrthancPlugins::RestApiGet(series, resourcePath, false)) { const std::string instance = series["Instances"][0].asString(); std::string sopClassUid; if (EducationConfiguration::GetInstance().HasPluginWholeSlideImaging() && OrthancPlugins::RestApiGetString(sopClassUid, "/instances/" + instance + "/metadata/SopClassUid", false) && Orthanc::Toolbox::StripSpaces(sopClassUid) == "1.2.840.10008.5.1.4.1.1.77.1.6") { // This is a microscopy image static const char* const FIELD_RESOLUTIONS = "Resolutions"; Json::Value pyramid; if (OrthancPlugins::RestApiGet(pyramid, "/wsi/pyramids/" + resourceId, headers, true) && pyramid.isMember(FIELD_RESOLUTIONS)) { const Json::Value& resolutions = pyramid[FIELD_RESOLUTIONS]; if (resolutions.type() == Json::arrayValue) { size_t largestIndex = 0; double largestValue = resolutions[0].asDouble(); for (Json::Value::ArrayIndex i = 1; i < resolutions.size(); i++) { double value = resolutions[i].asDouble(); if (value > largestValue) { largestIndex = i; largestValue = value; } } success = OrthancPlugins::RestApiGetString(preview, "/wsi/tiles/" + resourceId + "/" + boost::lexical_cast<std::string>(largestIndex) + "/0/0", headers, true); } } } if (!success) { success = OrthancPlugins::RestApiGetString(preview, "/instances/" + instance + "/preview", headers, false); } } break; } case Orthanc::ResourceType_Instance: success = OrthancPlugins::RestApiGetString(preview, resourcePath + "/preview", headers, false); break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } if (!success) { OrthancPlugins::OrthancImage white(OrthancPluginPixelFormat_Grayscale8, 128, 128); memset(white.GetBuffer(), 255, white.GetWidth() * white.GetHeight()); OrthancPlugins::MemoryBuffer jpeg; white.CompressJpegImage(jpeg, 70); jpeg.ToString(preview); } } std::string b64; Orthanc::Toolbox::EncodeBase64(b64, preview); Json::Value metadata = Json::objectValue; metadata["jpeg"] = b64; metadata["last-update"] = lastUpdate; Json::Value dummy; OrthancPlugins::RestApiPut(dummy, resourcePath + "/metadata/" + METADATA_PREVIEW, metadata.toStyledString(), false); HttpToolbox::AnswerBuffer(output, preview, Orthanc::MimeType_Jpeg); } static void GetResourceFromBody(Orthanc::ResourceType& level /* out */, std::string& resourceId /* out */, const Json::Value& body) { Json::Value resource; if (HttpToolbox::LookupJsonObject(resource, body, "resource")) { level = Orthanc::StringToResourceType(Orthanc::SerializationToolbox::ReadString(resource, "level").c_str()); resourceId = Orthanc::SerializationToolbox::ReadString(resource, "resource-id"); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } } void ChangeImageTitle(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Administrator); Orthanc::ResourceType level; std::string resourceId; GetResourceFromBody(level, resourceId, body); const std::string title = Orthanc::SerializationToolbox::ReadString(body, "title"); std::string path; switch (level) { case Orthanc::ResourceType_Study: path = "/studies/"; break; case Orthanc::ResourceType_Series: path = "/series/"; break; case Orthanc::ResourceType_Instance: path = "/instances/"; break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } path += resourceId + "/metadata/" + METADATA_INFO; Json::Value metadata; if (!OrthancPlugins::RestApiGet(metadata, path, false) || metadata.type() != Json::objectValue) { // The metadata has not been created yet metadata = Json::objectValue; } metadata["title"] = title; Json::Value dummy; if (OrthancPlugins::RestApiPut(metadata, path, metadata, false)) { metadata = Json::objectValue; HttpToolbox::AnswerText(output, ""); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } void LinkResourceWithProject(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Administrator); const std::string data = Orthanc::SerializationToolbox::ReadString(body, "data"); const std::string label = LABEL_PREFIX + Orthanc::SerializationToolbox::ReadString(body, "project"); Orthanc::ResourceType level; std::string resourceId; if (OrthancDatabase::LookupResourceByUserInput(level, resourceId, data)) { std::string path; switch (level) { case Orthanc::ResourceType_Study: path = "/studies/" + resourceId; break; case Orthanc::ResourceType_Series: path = "/series/" + resourceId; break; case Orthanc::ResourceType_Instance: path = "/instances/" + resourceId; break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } Json::Value dummy; if (OrthancPlugins::RestApiPut(dummy, path + "/labels/" + label, std::string(), false)) { HttpToolbox::AnswerText(output, ""); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } void ListImages(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Administrator); const std::string project = Orthanc::SerializationToolbox::ReadString(body, "project"); Json::Value answer; if (project == "_all-projects") { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } else if (project == "_no-project") { throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); } else { OrthancDatabase::FindResourcesInProject(answer, project); } HttpToolbox::AnswerJson(output, answer); } void UnlinkResourceFromProject(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user, const Json::Value& body) { assert(user.GetRole() == Role_Administrator); Orthanc::ResourceType level; std::string resourceId; GetResourceFromBody(level, resourceId, body); const std::string projectId = Orthanc::SerializationToolbox::ReadString(body, "project"); const std::string label = LABEL_PREFIX + projectId; std::string path; switch (level) { case Orthanc::ResourceType_Study: path = "/studies/" + resourceId + "/labels/" + label; break; case Orthanc::ResourceType_Series: path = "/series/" + resourceId + "/labels/" + label; break; case Orthanc::ResourceType_Instance: path = "/instances/" + resourceId + "/labels/" + label; break; default: throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); } if (OrthancPlugins::RestApiDelete(path, false)) { HttpToolbox::AnswerText(output, ""); } else { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } } void HandleProjectsConfiguration(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { assert(user.GetRole() == Role_Administrator); if (request->method == OrthancPluginHttpMethod_Get) { // List all the projects Json::Value projects = Json::arrayValue; { DocumentOrientedDatabase::Iterator iterator(ProjectPermissionContext::GetProjects()); while (iterator.Next()) { const Project& project = iterator.GetDocument<Project>(); Json::Value item; item["id"] = iterator.GetKey(); item["name"] = project.GetName(); item["description"] = project.GetDescription(); item["policy"] = EnumerationToString(project.GetPolicy()); item["primary_viewer"] = EnumerationToString(project.GetPrimaryViewer()); HttpToolbox::FormatViewers(item["secondary_viewers"], project.GetSecondaryViewers()); HttpToolbox::CopySetOfStrings(item["instructors"], project.GetInstructors()); HttpToolbox::CopySetOfStrings(item["learners"], project.GetLearners()); if (project.HasLtiContextId()) { item["lti_context_id"] = project.GetLtiContextId(); } projects.append(item); } } HttpToolbox::AnswerJson(output, projects); } else if (request->method == OrthancPluginHttpMethod_Post) { // Create a project Json::Value body; if (!Orthanc::Toolbox::ReadJson(body, request->body, request->bodySize)) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } else { std::unique_ptr<Project> project(new Project); project->SetName(Orthanc::SerializationToolbox::ReadString(body, "name")); project->SetDescription(Orthanc::SerializationToolbox::ReadString(body, "description")); std::set<ViewerType> viewers; EducationConfiguration::GetInstance().ListAvailableViewers(viewers); if (viewers.empty()) { // No viewer is installed, assume that Stone Web viewer is the default project->SetPrimaryViewer(ViewerType_StoneWebViewer); } else { project->SetSecondaryViewers(viewers); if (viewers.find(ViewerType_StoneWebViewer) != viewers.end()) { // Use Stone Web viewer as the default, as long as it is installed project->SetPrimaryViewer(ViewerType_StoneWebViewer); } else { // Stone is not installed, chose a random viewer project->SetPrimaryViewer(*viewers.begin()); } } Json::Value value; project->Serialize(value); ProjectPermissionContext::GetProjects().StoreWithAutoincrementedKey(project.release()); HttpToolbox::AnswerText(output, ""); } } else { OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET,POST"); } } void HandleSingleProject(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { assert(user.GetRole() == Role_Administrator); const std::string projectId(request->groups[0]); if (request->method == OrthancPluginHttpMethod_Get) { DocumentOrientedDatabase::Reader reader(ProjectPermissionContext::GetProjects()); const Project* project = reader.LookupDocument<Project>(projectId); if (project == NULL) { throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); } else { Json::Value answer; OrthancDatabase::FormatProjectWithResources(answer, projectId, *project); HttpToolbox::AnswerJson(output, answer); } } else if (request->method == OrthancPluginHttpMethod_Delete) { ProjectPermissionContext::GetProjects().Remove(projectId); HttpToolbox::AnswerText(output, ""); } else { OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "GET,DELETE"); } } void SetLtiClientId(OrthancPluginRestOutput* output, const std::string& url, const OrthancPluginHttpRequest* request, const AuthenticatedUser& user) { assert(user.GetRole() == Role_Administrator); if (request->method != OrthancPluginHttpMethod_Put) { OrthancPluginSendMethodNotAllowed(OrthancPlugins::GetGlobalContext(), output, "PUT"); } else { Json::Value body; if (!Orthanc::Toolbox::ReadJson(body, request->body, request->bodySize) || body.type() != Json::stringValue) { throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); } else { EducationConfiguration::GetInstance().SetLtiClientId(body.asString()); HttpToolbox::AnswerText(output, ""); } } } void RegisterEducationRestApiRoutes() { /** * Safe routes, accessible to any user (even if not logged in) **/ RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/dashboard.html"); RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/dashboard.js"); RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/list-projects.html"); RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/list-projects.js"); RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/login.js"); RestApiRouter::RegisterPublicGetRoute<ServeWebApplication>("/education/app/toolbox.js"); RestApiRouter::RegisterPublicPostRoute<DoLogin>("/education/do-login"); RestApiRouter::RegisterPublicGetRoute<DoLogout>("/education/do-logout"); // Those are safe routes, as they only generate a URL RestApiRouter::RegisterPublicPostRoute<GenerateListProjectUrl>("/education/api/list-project-url"); RestApiRouter::RegisterPublicPostRoute<GenerateViewerUrlFromResource>("/education/api/resource-viewer-url"); /** * Routes that necessitate user authentication (even as a guest) **/ RestApiRouter::RegisterAuthenticatedGetRoute<GetUserProjects>("/education/api/user-projects"); RestApiRouter::RegisterAuthenticatedGetRoute<RedirectRoot>("/"); RestApiRouter::RegisterAuthenticatedGetRoute<ServeConfiguration>("/education/api/config"); RestApiRouter::RegisterAuthenticatedGetRoute<ServeLogin>("/education/app/login.html"); RestApiRouter::RegisterAuthenticatedRoute<ChangeProjectParameter>("/education/api/projects/{}/{}"); RestApiRouter::RegisterAuthenticatedGetRoute< GeneratePreview<Orthanc::ResourceType_Study> >("/education/api/preview-study/{}"); RestApiRouter::RegisterAuthenticatedGetRoute< GeneratePreview<Orthanc::ResourceType_Series> >("/education/api/preview-series/{}"); RestApiRouter::RegisterAuthenticatedGetRoute< GeneratePreview<Orthanc::ResourceType_Instance> >("/education/api/preview-instance/{}"); /** * Routes that necessitate administrator credentials **/ RestApiRouter::RegisterAdministratorPostRoute<ChangeImageTitle>("/education/api/change-title"); RestApiRouter::RegisterAdministratorPostRoute<LinkResourceWithProject>("/education/api/link"); RestApiRouter::RegisterAdministratorPostRoute<ListImages>("/education/api/list-images"); RestApiRouter::RegisterAdministratorPostRoute<UnlinkResourceFromProject>("/education/api/unlink"); RestApiRouter::RegisterAdministratorRoute<HandleProjectsConfiguration>("/education/api/projects"); RestApiRouter::RegisterAdministratorRoute<HandleSingleProject>("/education/api/projects/{}"); RestApiRouter::RegisterAdministratorRoute<SetLtiClientId>("/education/api/config/lti-client-id"); } AuthenticatedUser* AuthenticateFromEducationCookie(const std::list<HttpToolbox::Cookie>& cookies) { for (std::list<HttpToolbox::Cookie>::const_iterator it = cookies.begin(); it != cookies.end(); ++it) { if (it->GetKey() == COOKIE_USER_AUTH) { try { return AuthenticatedUser::FromJWT(EducationConfiguration::GetInstance().GetLtiContext(), it->GetValue()); } catch (Orthanc::OrthancException&) { } } } return NULL; }
