Mercurial > hg > orthanc-authorization
annotate Plugin/CachedAuthorizationService.cpp @ 202:3c56c3f0059a default tip
Fix forbidden access when the PatientID and StudyInstanceUID are identical
author | Alain Mazy <am@orthanc.team> |
---|---|
date | Mon, 23 Sep 2024 12:43:33 +0200 |
parents | c4b908970ae4 |
children |
rev | line source |
---|---|
1 | 1 /** |
2 * Advanced authorization plugin for Orthanc | |
68 | 3 * Copyright (C) 2017-2023 Osimis S.A., Belgium |
150 | 4 * Copyright (C) 2024-2024 Orthanc Team SRL, Belgium |
188
c4b908970ae4
updated copyright, as Orthanc Team now replaces Osimis
Sebastien Jodogne <s.jodogne@gmail.com>
parents:
150
diff
changeset
|
5 * Copyright (C) 2021-2024 Sebastien Jodogne, ICTEAM UCLouvain, Belgium |
1 | 6 * |
7 * This program is free software: you can redistribute it and/or | |
8 * modify it under the terms of the GNU Affero General Public License | |
9 * as published by the Free Software Foundation, either version 3 of | |
10 * the License, or (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, but | |
13 * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Affero General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Affero General Public License | |
18 * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 **/ | |
20 | |
21 #include "CachedAuthorizationService.h" | |
115
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
22 #include "AuthorizationWebService.h" |
1 | 23 |
32 | 24 #include <OrthancException.h> |
71 | 25 #include <Toolbox.h> |
1 | 26 |
27 #include <boost/lexical_cast.hpp> | |
28 | |
29 namespace OrthancPlugins | |
30 { | |
31 std::string CachedAuthorizationService::ComputeKey(OrthancPluginHttpMethod method, | |
32 const AccessedResource& access, | |
72
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
33 const Token* token, |
1 | 34 const std::string& tokenValue) const |
35 { | |
72
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
36 if (token != NULL) |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
37 { |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
38 return (boost::lexical_cast<std::string>(method) + "|" + |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
39 boost::lexical_cast<std::string>(access.GetLevel()) + "|" + |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
40 access.GetOrthancId() + "|" + token->GetKey() + "|" + tokenValue); |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
41 } |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
42 else |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
43 { |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
44 return (boost::lexical_cast<std::string>(method) + "|" + |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
45 boost::lexical_cast<std::string>(access.GetLevel()) + "|" + |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
46 access.GetOrthancId() + "|anonymous"); |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
47 } |
1 | 48 } |
49 | |
50 | |
71 | 51 std::string CachedAuthorizationService::ComputeKey(const std::string& permission, |
72
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
52 const Token* token, |
71 | 53 const std::string& tokenValue) const |
54 { | |
72
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
55 if (token != NULL) |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
56 { |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
57 return (permission + "|" + token->GetKey() + "|" + tokenValue); |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
58 } |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
59 else |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
60 { |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
61 return (permission + "|anonymous"); |
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
62 } |
71 | 63 } |
64 | |
65 | |
66 CachedAuthorizationService::CachedAuthorizationService(BaseAuthorizationService* decorated /* takes ownership */, | |
1 | 67 ICacheFactory& factory) : |
68 decorated_(decorated), | |
69 cache_(factory.Create()) | |
70 { | |
71 if (decorated_.get() == NULL) | |
72 { | |
73 throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); | |
74 } | |
75 } | |
76 | |
77 | |
71 | 78 bool CachedAuthorizationService::IsGrantedInternal(unsigned int& validity, |
79 OrthancPluginHttpMethod method, | |
80 const AccessedResource& access, | |
81 const Token* token, | |
82 const std::string& tokenValue) | |
1 | 83 { |
84 assert(decorated_.get() != NULL); | |
85 | |
72
e381ba725669
new PUT auth/tokens/{token-type} API route + updated interface with WebService
Alain Mazy <am@osimis.io>
parents:
71
diff
changeset
|
86 std::string key = ComputeKey(method, access, token, tokenValue); |
1 | 87 std::string value; |
88 | |
89 if (cache_->Retrieve(value, key)) | |
90 { | |
91 // Return the previously cached value | |
92 return (value == "1"); | |
93 } | |
94 | |
71 | 95 bool granted = decorated_->IsGrantedInternal(validity, method, access, token, tokenValue); |
1 | 96 |
97 if (granted) | |
98 { | |
99 if (validity > 0) | |
100 { | |
101 cache_->Store(key, "1", validity); | |
102 } | |
103 | |
104 return true; | |
105 } | |
106 else | |
107 { | |
108 if (validity > 0) | |
109 { | |
110 cache_->Store(key, "0", validity); | |
111 } | |
112 | |
113 return false; | |
114 } | |
115 } | |
116 | |
117 | |
115
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
118 bool CachedAuthorizationService::GetUserProfileInternal(unsigned int& validityNotUsed, |
109 | 119 UserProfile& profile /* out */, |
71 | 120 const Token* token, |
121 const std::string& tokenValue) | |
122 { | |
115
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
123 assert(decorated_.get() != NULL); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
124 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
125 std::string key = ComputeKey("user-profile", token, tokenValue); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
126 std::string serializedProfile; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
127 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
128 if (cache_->Retrieve(serializedProfile, key)) |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
129 { |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
130 // Return the previously cached profile |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
131 Json::Value jsonProfile; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
132 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
133 Orthanc::Toolbox::ReadJson(jsonProfile, serializedProfile); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
134 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
135 AuthorizationWebService::FromJson(profile, jsonProfile); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
136 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
137 profile.tokenKey = token->GetKey(); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
138 profile.tokenType = token->GetType(); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
139 profile.tokenValue = tokenValue; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
140 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
141 return true; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
142 } |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
143 else |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
144 { |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
145 unsigned int validity; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
146 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
147 if (decorated_->GetUserProfileInternal(validity, profile, token, tokenValue)) |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
148 { |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
149 Json::Value jsonProfile; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
150 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
151 AuthorizationWebService::ToJson(jsonProfile, profile); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
152 Orthanc::Toolbox::WriteFastJson(serializedProfile, jsonProfile); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
153 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
154 cache_->Store(key, serializedProfile, validity); |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
155 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
156 return true; |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
157 } |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
158 } |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
159 |
0eed78c1e177
cache the UserProfile + updated http filter logic
Alain Mazy <am@osimis.io>
parents:
113
diff
changeset
|
160 return false; |
71 | 161 } |
162 | |
163 bool CachedAuthorizationService::HasUserPermissionInternal(unsigned int& validity, | |
164 const std::string& permission, | |
113 | 165 const UserProfile& profile) |
1 | 166 { |
167 assert(decorated_.get() != NULL); | |
168 | |
113 | 169 Token token(profile.tokenType, profile.tokenKey); |
170 std::string key = ComputeKey(permission, &token, profile.tokenValue); | |
71 | 171 std::string value; |
172 | |
173 if (cache_->Retrieve(value, key)) | |
174 { | |
175 // Return the previously cached value | |
176 return (value == "1"); | |
177 } | |
178 | |
113 | 179 bool granted = decorated_->HasUserPermissionInternal(validity, permission, profile); |
71 | 180 |
181 if (granted) | |
182 { | |
183 if (validity > 0) | |
184 { | |
185 cache_->Store(key, "1", validity); | |
186 } | |
187 | |
188 return true; | |
189 } | |
190 else | |
191 { | |
192 if (validity > 0) | |
193 { | |
194 cache_->Store(key, "0", validity); | |
195 } | |
196 | |
197 return false; | |
198 } | |
1 | 199 } |
69
af44dce56328
new 'auth/user-profile' Rest API route
Alain Mazy <am@osimis.io>
parents:
68
diff
changeset
|
200 |
71 | 201 |
69
af44dce56328
new 'auth/user-profile' Rest API route
Alain Mazy <am@osimis.io>
parents:
68
diff
changeset
|
202 |
1 | 203 } |