changeset 113:43154740ea2e

wip: checking labels
author Alain Mazy <am@osimis.io>
date Tue, 05 Sep 2023 12:48:20 +0200
parents 572955904411
children 546aea509427
files Plugin/AuthorizationWebService.cpp Plugin/AuthorizationWebService.h Plugin/BaseAuthorizationService.h Plugin/CachedAuthorizationService.cpp Plugin/CachedAuthorizationService.h Plugin/Enumerations.h Plugin/IAuthorizationService.h Plugin/Plugin.cpp Plugin/Token.cpp Plugin/Token.h
diffstat 10 files changed, 154 insertions(+), 68 deletions(-) [+]
line wrap: on
line diff
--- a/Plugin/AuthorizationWebService.cpp	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/AuthorizationWebService.cpp	Tue Sep 05 12:48:20 2023 +0200
@@ -389,7 +389,10 @@
       validity = jsonProfile[VALIDITY].asUInt();
       
       profile.name = jsonProfile[USER_NAME].asString();
-      
+      profile.tokenKey = token->GetKey();
+      profile.tokenType = token->GetType();
+      profile.tokenValue = tokenValue;
+
       for (Json::ArrayIndex i = 0; i < jsonProfile[PERMISSIONS].size(); ++i)
       {
         profile.permissions.insert(jsonProfile[PERMISSIONS][i].asString());
@@ -414,21 +417,14 @@
 
   bool AuthorizationWebService::HasUserPermissionInternal(unsigned int& validity,
                                                           const std::string& permission,
-                                                          const Token* token,
-                                                          const std::string& tokenValue)
+                                                          const UserProfile& profile)
   {
-    UserProfile profile;
-
-
-    if (GetUserProfileInternal(validity, profile, token, tokenValue))
+    const std::set<std::string>& permissions = profile.permissions;
+    for (std::set<std::string>::const_iterator it = permissions.begin(); it != permissions.end(); ++it)
     {
-      std::set<std::string>& permissions = profile.permissions;
-      for (std::set<std::string>::const_iterator it = permissions.begin(); it != permissions.end(); ++it)
+      if (permission == *it)
       {
-        if (permission == *it)
-        {
-          return true;
-        }
+        return true;
       }
     }
 
--- a/Plugin/AuthorizationWebService.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/AuthorizationWebService.h	Tue Sep 05 12:48:20 2023 +0200
@@ -42,14 +42,13 @@
                            const std::string& tokenValue) ORTHANC_OVERRIDE;
     
     virtual bool GetUserProfileInternal(unsigned int& validity,
-                                UserProfile& profile /* out */,
-                                const Token* token,
-                                const std::string& tokenValue) ORTHANC_OVERRIDE;
+                                        UserProfile& profile /* out */,
+                                        const Token* token,
+                                        const std::string& tokenValue) ORTHANC_OVERRIDE;
 
     virtual bool HasUserPermissionInternal(unsigned int& validity,
-                                   const std::string& permission,
-                                   const Token* token,
-                                   const std::string& tokenValue) ORTHANC_OVERRIDE;
+                                           const std::string& permission,
+                                           const UserProfile& profile) ORTHANC_OVERRIDE;
   
   public:
     AuthorizationWebService(const std::string& tokenValidationUrl, 
--- a/Plugin/BaseAuthorizationService.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/BaseAuthorizationService.h	Tue Sep 05 12:48:20 2023 +0200
@@ -42,8 +42,7 @@
 
     virtual bool HasUserPermissionInternal(unsigned int& validity,
                                            const std::string& permission,
-                                           const Token* token,
-                                           const std::string& tokenValue) = 0;
+                                           const UserProfile& profile) = 0;
 
   public:
     virtual ~BaseAuthorizationService()
@@ -82,8 +81,7 @@
 
     virtual bool HasUserPermission(unsigned int& validity /* out */,
                                    const std::set<std::string>& anyOfPermissions,
-                                   const Token& token,
-                                   const std::string& tokenValue)
+                                   const UserProfile& profile)
     {
       if (anyOfPermissions.size() == 0)
       {
@@ -92,7 +90,7 @@
 
       for (std::set<std::string>::const_iterator it = anyOfPermissions.begin(); it != anyOfPermissions.end(); ++it)
       {
-        if (HasUserPermissionInternal(validity, *it, &token, tokenValue))
+        if (HasUserPermissionInternal(validity, *it, profile))
         {
           return true;
         }
@@ -108,9 +106,12 @@
         return true;
       }
 
+      UserProfile anonymousUserProfile;
+      anonymousUserProfile.tokenType = TokenType_None;
+
       for (std::set<std::string>::const_iterator it = anyOfPermissions.begin(); it != anyOfPermissions.end(); ++it)
       {
-        if (HasUserPermissionInternal(validity, *it, NULL, ""))
+        if (HasUserPermissionInternal(validity, *it, anonymousUserProfile))
         {
           return true;
         }
--- a/Plugin/CachedAuthorizationService.cpp	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/CachedAuthorizationService.cpp	Tue Sep 05 12:48:20 2023 +0200
@@ -123,12 +123,12 @@
 
   bool CachedAuthorizationService::HasUserPermissionInternal(unsigned int& validity,
                                                              const std::string& permission,
-                                                             const Token* token,
-                                                             const std::string& tokenValue)
+                                                             const UserProfile& profile)
   {
     assert(decorated_.get() != NULL);
 
-    std::string key = ComputeKey(permission, token, tokenValue);
+    Token token(profile.tokenType, profile.tokenKey);
+    std::string key = ComputeKey(permission, &token, profile.tokenValue);
     std::string value;
 
     if (cache_->Retrieve(value, key))
@@ -137,7 +137,7 @@
       return (value == "1");
     }        
         
-    bool granted = decorated_->HasUserPermissionInternal(validity, permission, token, tokenValue);
+    bool granted = decorated_->HasUserPermissionInternal(validity, permission, profile);
 
     if (granted)
     {
--- a/Plugin/CachedAuthorizationService.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/CachedAuthorizationService.h	Tue Sep 05 12:48:20 2023 +0200
@@ -58,8 +58,7 @@
 
     virtual bool HasUserPermissionInternal(unsigned int& validity,
                                            const std::string& permission,
-                                           const Token* token,
-                                           const std::string& tokenValue) ORTHANC_OVERRIDE;
+                                           const UserProfile& profile) ORTHANC_OVERRIDE;
 
 
   public:
--- a/Plugin/Enumerations.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/Enumerations.h	Tue Sep 05 12:48:20 2023 +0200
@@ -25,7 +25,9 @@
   enum TokenType
   {
     TokenType_HttpHeader,
-    TokenType_GetArgument
+    TokenType_GetArgument,
+
+    TokenType_None
   };
 
 
--- a/Plugin/IAuthorizationService.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/IAuthorizationService.h	Tue Sep 05 12:48:20 2023 +0200
@@ -58,6 +58,11 @@
       std::string name;
       std::set<std::string> permissions;
       std::set<std::string> authorizedLabels;
+
+      // the source token key/value that identified the user
+      TokenType   tokenType;
+      std::string tokenKey;
+      std::string tokenValue;
     };
 
     virtual ~IAuthorizationService()
@@ -84,8 +89,7 @@
 
     virtual bool HasUserPermission(unsigned int& validity /* out */,
                                    const std::set<std::string>& anyOfPermissions,
-                                   const Token& token,
-                                   const std::string& tokenValue) = 0;
+                                   const UserProfile& profile) = 0;
 
     virtual bool HasAnonymousUserPermission(unsigned int& validity /* out */,
                                             const std::set<std::string>& anyOfPermissions) = 0;
--- a/Plugin/Plugin.cpp	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/Plugin.cpp	Tue Sep 05 12:48:20 2023 +0200
@@ -73,6 +73,71 @@
   }
 };
 
+bool HasAccessToAllLabels(const OrthancPlugins::IAuthorizationService::UserProfile& profile)
+{
+  return (profile.authorizedLabels.find("*") != profile.authorizedLabels.end());
+}
+
+bool HasAccessToSomeLabels(const OrthancPlugins::IAuthorizationService::UserProfile& profile)
+{
+  return (profile.authorizedLabels.size() > 0);
+}
+
+
+static bool CheckAuthorizedLabelsForResource(const std::string& uri,
+                                             const OrthancPlugins::AssociativeArray& getArguments,
+                                             const OrthancPlugins::IAuthorizationService::UserProfile& profile)
+{
+  if (HasAccessToAllLabels(profile))
+  {
+    return true;
+  }
+
+  if (authorizationParser_.get() != NULL &&
+      authorizationService_.get() != NULL)
+  {
+    // Parse the resources that are accessed through this URI
+    OrthancPlugins::IAuthorizationParser::AccessedResources accesses;
+
+    if (!authorizationParser_->Parse(accesses, uri, getArguments.GetMap()))
+    {
+      return false;  // Unable to parse this URI
+    }
+
+    // Loop over all the accessed resources to ensure access is
+    // granted to each of them
+    for (OrthancPlugins::IAuthorizationParser::AccessedResources::const_iterator
+        access = accesses.begin(); access != accesses.end(); ++access)
+    {
+      // Ignored the access levels that are unchecked
+      // (cf. "UncheckedLevels" option)
+      if (uncheckedLevels_.find(access->GetLevel()) == uncheckedLevels_.end())
+      {
+        std::string msg = std::string("Testing whether access to ") + OrthancPlugins::EnumerationToString(access->GetLevel()) + " \"" + access->GetOrthancId() + "\" is allowed wrt Labels for User '" + profile.name + "'";
+        const std::set<std::string>& resourceLabels = access->GetLabels();
+        std::set<std::string> authorizedResourceLabels;
+
+        Orthanc::Toolbox::GetIntersection(authorizedResourceLabels, resourceLabels, profile.authorizedLabels);
+
+        if (authorizedResourceLabels.size() == 0)
+        {
+          LOG(INFO) << msg << " -> not granted, no authorized labels";
+          return false;
+        }
+        else
+        {
+          LOG(INFO) << msg << " -> granted, at least one authorized labels";
+          return true;
+        }
+      }
+    }
+
+    // Access is granted to all the resources that are 'unchecked'
+    return true;
+  }
+
+  return false;  // TODO or true ???
+}
 
 static int32_t FilterHttpRequests(OrthancPluginHttpMethod method,
                                   const char *uri,
@@ -154,16 +219,26 @@
         if (authTokens.empty())
         {
           std::string msg = std::string("Testing whether anonymous user has any of the required permissions '") + JoinStrings(requiredPermissions) + "'";
-          LOG(INFO) << msg;
-          if (authorizationService_->HasAnonymousUserPermission(validity, requiredPermissions))
-          {
-            LOG(INFO) << msg << " -> granted";
-            return 1;
-          }
-          else
-          {
-            LOG(INFO) << msg << " -> not granted";
-          }
+          
+          // TODO: how to handle anonymous user ?
+          
+          // LOG(INFO) << msg;
+          // if (authorizationService_->HasAnonymousUserPermission(validity, requiredPermissions))
+          // {
+          //     // TODO: check labels permissions
+          //   LOG(INFO) << msg << " -> granted";
+
+          //   if (CheckAuthorizedLabelsForResource(uri, getArguments, profile))
+          //   {
+          //     return 1;
+          //   }
+          // }
+          // else
+          // {
+          //   LOG(INFO) << msg << " -> not granted";
+          // }
+          LOG(INFO) << msg << " -> not granted, TODO ????";
+          return 0;
         }
         else
         {
@@ -172,15 +247,25 @@
             std::string msg = std::string("Testing whether user has the required permissions '") + JoinStrings(requiredPermissions) + "' based on the HTTP header '" + authTokens[i].GetToken().GetKey() + "' required to match '" + matchedPattern + "'";
 
             LOG(INFO) << msg;
-            if (authorizationService_->HasUserPermission(validity, requiredPermissions, authTokens[i].GetToken(), authTokens[i].GetValue()))
+            
+            OrthancPlugins::IAuthorizationService::UserProfile profile;
+            unsigned int validityNotUsed;
+            authorizationService_->GetUserProfile(validityNotUsed, profile, authTokens[i].GetToken(), authTokens[i].GetValue());
+
+            if (authorizationService_->HasUserPermission(validity, requiredPermissions, profile))
             {
-              // TODO: check labels permissions
               LOG(INFO) << msg << " -> granted";
-              return 1;
+
+              // check labels permissions
+              if (CheckAuthorizedLabelsForResource(uri, getArguments, profile))
+              {
+                return 1;
+              }
+              // not granted, but continue and check if a resource tokens grant access
             }
             else
             {
-              LOG(INFO) << msg << " -> not granted";
+              LOG(INFO) << msg << " -> not granted";  // but continue and check if a resource tokens grant access
             }
           }
         }
@@ -388,16 +473,6 @@
   return false;
 }
 
-bool HasAccessToAllLabels(const OrthancPlugins::IAuthorizationService::UserProfile& profile)
-{
-  return (profile.authorizedLabels.find("*") != profile.authorizedLabels.end());
-}
-
-bool HasAccessToSomeLabels(const OrthancPlugins::IAuthorizationService::UserProfile& profile)
-{
-  return (profile.authorizedLabels.size() > 0);
-}
-
 void AdjustToolsFindQueryLabels(Json::Value& query, const OrthancPlugins::IAuthorizationService::UserProfile& profile)
 {
   std::set<std::string> labelsToFind;
@@ -461,15 +536,11 @@
       {
         if (profile.authorizedLabels.size() > 0)
         {
-          throw Orthanc::OrthancException(Orthanc::ErrorCode_Unauthorized, "Auth plugin: unable to transform tools/find query with 'None' labels constraint when the user only has authorized_labels.");        
+          throw Orthanc::OrthancException(Orthanc::ErrorCode_Unauthorized, "Auth plugin: unable to transform tools/find query with 'None' labels constraint when the user only has authorized_labels.");
         }
       }
     }
   }
-  else
-  {
-    // TODO what shall we do if the user has no authorized_labels ???
-  }
 }
 
 void ToolsFind(OrthancPluginRestOutput* output,
@@ -507,7 +578,7 @@
     }
     else
     {
-      OrthancPluginSendHttpStatusCode(context, output, 403); // TODO: check
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_Unauthorized, "Auth plugin: no user profile found, access to tools/find is forbidden.");
     }
   }
 }
@@ -556,10 +627,8 @@
     }
     else
     {
-      OrthancPluginSendHttpStatusCode(context, output, 403); // TODO: check
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_Unauthorized, "Auth plugin: no user profile found, access to tools/labels is forbidden.");
     }
-
-
   }
 }
 
@@ -945,7 +1014,6 @@
         if (!urlTokenCreationBase.empty())
         {
           LOG(WARNING) << "Authorization plugin: base url defined for Token Creation : " << urlTokenCreationBase;
-          // TODO Token Creation
         }
         else
         {
@@ -1012,24 +1080,28 @@
           OrthancPlugins::AccessLevel checkedLevel = OrthancPlugins::StringToAccessLevel(checkedLevelString);
           if (checkedLevel == OrthancPlugins::AccessLevel_Instance) 
           {
+            uncheckedLevels_.insert(OrthancPlugins::AccessLevel_System);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Patient);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Study);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Series);
           }
           else if (checkedLevel == OrthancPlugins::AccessLevel_Series) 
           {
+            uncheckedLevels_.insert(OrthancPlugins::AccessLevel_System);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Patient);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Study);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Instance);
           }
           else if (checkedLevel == OrthancPlugins::AccessLevel_Study) 
           {
+            uncheckedLevels_.insert(OrthancPlugins::AccessLevel_System);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Patient);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Series);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Instance);
           }
           else if (checkedLevel == OrthancPlugins::AccessLevel_Patient) 
           {
+            uncheckedLevels_.insert(OrthancPlugins::AccessLevel_System);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Study);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Series);
             uncheckedLevels_.insert(OrthancPlugins::AccessLevel_Instance);
--- a/Plugin/Token.cpp	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/Token.cpp	Tue Sep 05 12:48:20 2023 +0200
@@ -32,4 +32,15 @@
       throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
     }
   }
+
+  Token::Token(const Token& other) :
+    type_(other.GetType()),
+    key_(other.GetKey())
+  {
+    if (key_.empty())
+    {
+      throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange);
+    }
+  }
+
 }
--- a/Plugin/Token.h	Thu Aug 31 16:51:15 2023 +0200
+++ b/Plugin/Token.h	Tue Sep 05 12:48:20 2023 +0200
@@ -32,6 +32,8 @@
     Token(TokenType type,
           const std::string& key);
 
+    Token(const Token& other);
+
     TokenType GetType() const
     {
       return type_;