This repository has been archived by the owner on Sep 23, 2020. It is now read-only.
/
AuthzDecisionLogic.java
421 lines (376 loc) · 14.5 KB
/
AuthzDecisionLogic.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
package org.globus.workspace.sqlauthz;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.workspace.RepoFileSystemAdaptor;
import org.globus.workspace.WorkspaceException;
import org.globus.workspace.groupauthz.DecisionLogic;
import org.globus.workspace.groupauthz.GroupRights;
import org.globus.workspace.persistence.WorkspaceDatabaseException;
import org.globus.workspace.service.binding.vm.VirtualMachinePartition;
import org.nimbustools.api.services.rm.AuthorizationException;
import org.nimbustools.api.services.rm.ResourceRequestDeniedException;
import org.nimbus.authz.AuthzDBAdapter;
import org.nimbus.authz.AuthzDBException;
import javax.sql.DataSource;
import java.io.File;
/**
* Created by John Bresnahan
* User: bresnaha
* Date: May 20, 2010
* Time: 6:41:26 AM
* <p/>
* org.globus.workspace.sqlauthz
*/
public class AuthzDecisionLogic extends DecisionLogic
implements RepoFileSystemAdaptor
{
private static final Log logger =
LogFactory.getLog(AuthzDecisionLogic.class.getName());
protected AuthzDBAdapter authDB;
private String repoScheme = null;
private String repoHost = null;
private String repoDir = null;
private boolean schemePassthrough;
public AuthzDecisionLogic(
DataSource ds,
String schemePassthroughStr)
{
this.authDB = new AuthzDBAdapter(ds);
this.schemePassthrough =
schemePassthroughStr != null
&& schemePassthroughStr.trim().equalsIgnoreCase("true");
}
public String translateExternaltoInternal(
String publicUrl)
throws WorkspaceException
{
String rc = null;
try
{
String [] urlParts = parseUrl(publicUrl);
String scheme = urlParts[0];
String hostport = urlParts[1];
String objectname = urlParts[2];
// hinge on scheme, perhaps set up fancy interface plugin decision later
if(scheme.equals("cumulus"))
{
rc = this.translateCumulus(hostport, objectname);
}
else
{
rc = publicUrl;
}
}
catch(Exception ex)
{
throw new WorkspaceException("error translating cumulus name", ex);
}
if(rc == null)
{
throw new WorkspaceException("external image scheme " + publicUrl + " is not supported");
}
return rc;
}
protected String translateCumulus(
String hostport,
String objectName)
throws AuthorizationException
{
try
{
int [] fileIds = this.cumulusGetFileID(hostport, objectName);
if(fileIds[1] < 0)
{
throw new AuthorizationException("The file is not found and thus cannot be translated " + objectName);
}
String dataKey = this.authDB.getDataKey(fileIds[1]);
String rc = this.getRepoScheme() + this.getRepoHost() + "/" + dataKey;
logger.debug("converted " + objectName + " to " + rc);
return rc;
}
catch(AuthzDBException wsdbex)
{
logger.error("iternal db problem", wsdbex);
throw new AuthorizationException("Internal problem with the data base " + wsdbex.toString());
}
}
private String [] parseUrl(
String url)
throws AuthorizationException
{
String [] results = url.split("://", 2);
if(results == null || results.length != 2)
{
throw new AuthorizationException("Poorly formed repository url, no scheme " + url);
}
String scheme = results[0];
String remaining = results[1];
results = remaining.split("/", 2);
if(results == null || results.length != 2)
{
throw new AuthorizationException("Poorly formed repository url, no host separator " + url);
}
String hostname = results[0];
String objectName = results[1];
results = new String[3];
results[0] = scheme;
results[1] = hostname;
results[2] = objectName;
return results;
}
private int [] cumulusGetFileID(
String hostport,
String objectName)
throws AuthorizationException
{
String bucketName;
String keyName;
String [] results = objectName.split("/", 2);
if(results == null || results.length != 2)
{
throw new AuthorizationException("Poorly formed bucket/key " + objectName);
}
bucketName = results[0];
keyName = results[1];
try
{
int parentId = authDB.getFileID(bucketName, -1, AuthzDBAdapter.OBJECT_TYPE_S3);
if (parentId < 0)
{
throw new AuthorizationException("No such bucket " + bucketName);
}
int fileId = authDB.getFileID(keyName, parentId, AuthzDBAdapter.OBJECT_TYPE_S3);
int [] rc = new int[2];
rc[0] = parentId;
rc[1] = fileId;
return rc;
}
catch(AuthzDBException wsdbex)
{
logger.error("trouble looking up the cumulus information ", wsdbex);
throw new AuthorizationException("Trouble with the database " + wsdbex.toString());
}
}
protected void checkImages(
VirtualMachinePartition[] parts,
GroupRights rights,
StringBuffer buf,
String dn,
String dnhash)
throws AuthorizationException,
ResourceRequestDeniedException
{
boolean different_target = false;
String unPropImageName = null;
for (int i = 0; i < parts.length; i++)
{
if (!parts[i].isPropRequired() && !parts[i].isUnPropRequired())
{
logger.debug("groupauthz not examining '" +
parts[i].getImage() + "': no prop/unprop needed");
continue;
}
String incomingImageName = parts[i].getImage();
if(parts[i].isPropRequired())
{
unPropImageName = parts[i].getAlternateUnpropTarget();
if(unPropImageName == null)
{
unPropImageName = incomingImageName;
}
else
{
different_target = true;
}
}
logger.debug("Image " + incomingImageName + " requested");
logger.debug("Unprop image " + unPropImageName + " requested");
try
{
// see if we are allowed to read the image
long size = checkUrl(incomingImageName, dn, false, 0);
// if unpropagting, see if we are allowed to write to the unprop name
if(unPropImageName != null)
{
checkUrl(unPropImageName, dn, true, size);
}
}
catch (WorkspaceDatabaseException e)
{
final String msg = "ERROR: Partition in " +
"binding is not a valid URI? Can't make decision. " +
" Error message: " + e.getMessage();
buf.append(msg);
logger.error(buf.toString(), e);
throw new AuthorizationException(msg);
}
}
}
private long checkUrl(
String url,
String userId,
boolean write,
long expectedSize)
throws AuthorizationException, ResourceRequestDeniedException, WorkspaceDatabaseException
{
int fileId;
String [] urlParts = this.parseUrl(url);
String scheme = urlParts[0];
String hostport = urlParts[1];
String objectName = urlParts[2];
int schemeType = -1;
// Here would be a good place to hindge on scheme. We could make a plugin interface
// that allowed namespace conversion based on scheme and hostname:port. Hostname is
// also needed because it is possible to want to do something different for cumulus://hostA/file
// than cumulus://hostB/file... for that matter we may also want to hindge on file path so we probably
// need an interface to do regex matching. all of that is a bit over engineered for now it will
// be just cumulus and file
if(scheme.equals("cumulus"))
{
schemeType = AuthzDBAdapter.OBJECT_TYPE_S3;
try
{
String canUser = authDB.getCanonicalUserIdFromDn(userId);
int [] fileIds = this.cumulusGetFileID(hostport, objectName);
String [] results = objectName.split("/", 2);
String bucketName = results[0];
String keyName = results[1];
boolean checkSpace = false;
if(fileIds[0] < 0)
{
throw new ResourceRequestDeniedException("The bucket name " + bucketName + " was not found.");
}
String perms = "";
if(fileIds[1] < 0 && write)
{
String dataKey = this.getRepoDir() + "/" + objectName.replace("/", "__");
logger.debug("Adding new datakey " + dataKey);
fileIds[1] = authDB.newFile(keyName, fileIds[0], canUser, dataKey, schemeType);
}
perms = authDB.getPermissions(fileIds[1], canUser);
if(fileIds[1] < 0)
{
throw new ResourceRequestDeniedException("the object " + objectName + " was not found.");
}
int ndx = perms.indexOf('r');
if(ndx < 0)
{
throw new ResourceRequestDeniedException("user " + userId + " canonical ID " + canUser + " does not have read access to " + url);
}
long size = authDB.getFileSize(fileIds[1]);
if(write)
{
ndx = perms.indexOf('w');
if(ndx < 0)
{
throw new ResourceRequestDeniedException("user " + userId + " does not have write access to " + url);
}
// expected size is only zero when replacing the original file. in this case we assume it will
// fit
if(expectedSize != 0)
{
// deduct the size of the file that already exists from the expected size. it
// is ok if it goes negative
long canFitSize = expectedSize - size;
boolean quota = authDB.canStore(canFitSize, canUser, schemeType);
if(!quota)
{
throw new ResourceRequestDeniedException("You do not have enough storage space for the new image. Please free up some storage and try again");
}
}
}
return size;
}
catch(AuthzDBException wsdbex)
{
logger.error("iternal db problem", wsdbex);
throw new AuthorizationException("Internal problem with the data base " + wsdbex.toString());
}
}
else if (scheme.equals("file"))
{
return 0;
}
else if (this.schemePassthrough)
{
return 0;
}
else
{
throw new ResourceRequestDeniedException("scheme of: " + scheme + " is not supported.");
}
}
public void setRepoScheme(String repoScheme)
{
this.repoScheme = repoScheme;
}
public String getRepoScheme()
{
return this.repoScheme;
}
public void setRepoHost(String repoHost)
{
this.repoHost = repoHost;
}
public String getRepoHost()
{
return this.repoHost;
}
public void setRepoDir(String repoDir)
{
this.repoDir = repoDir;
}
public String getRepoDir()
{
return this.repoDir;
}
public void unpropagationFinished(
String publicName)
throws WorkspaceException
{
try
{
String [] urlParts = this.parseUrl(publicName);
String scheme = urlParts[0];
String hostport = urlParts[1];
String objectName = urlParts[2];
int schemeType = -1;
if(scheme.equals("cumulus"))
{
schemeType = AuthzDBAdapter.OBJECT_TYPE_S3;
int [] fileIds = this.cumulusGetFileID(hostport, objectName);
String datakey = authDB.getDataKey(fileIds[1]);
// need to calculate the md5sum and set the size
// for now lets just set the size
File f = new File(datakey);
long size = f.length();
long expectedSize = authDB.getFileSize(fileIds[1]);
long sizeDiff = size - expectedSize;
// if the size of the file grew from what it was expected to be we must make sure the quota
// is not busted
if(sizeDiff > 0)
{
String canUser = authDB.getFileOwner(fileIds[1]);
boolean hasRoom = authDB.canStore(sizeDiff, canUser, schemeType);
if(!hasRoom)
{
logger.error("FOR TIMF callout happens here");
}
}
authDB.setFileSize(fileIds[1], size);
}
else
{
return;
}
}
catch(AuthorizationException authex)
{
throw new WorkspaceException("Authorization exception occured ", authex);
}
catch(AuthzDBException wsdbex)
{
throw new WorkspaceException("Workspace database exception occured ", wsdbex);
}
}
}