4141import io .cdap .cdap .etl .proto .connection .SampleResponse ;
4242import io .cdap .cdap .features .Feature ;
4343import io .cdap .cdap .internal .io .SchemaTypeAdapter ;
44+ import io .cdap .cdap .proto .element .EntityType ;
4445import io .cdap .cdap .proto .id .NamespaceId ;
46+ import io .cdap .cdap .proto .security .StandardPermission ;
47+ import io .cdap .cdap .security .spi .authorization .ContextAccessEnforcer ;
4548import io .cdap .wrangler .PropertyIds ;
4649import io .cdap .wrangler .RequestExtractor ;
4750import io .cdap .wrangler .api .DirectiveConfig ;
5861import io .cdap .wrangler .parser .MigrateToV2 ;
5962import io .cdap .wrangler .parser .RecipeCompiler ;
6063import io .cdap .wrangler .proto .BadRequestException ;
64+ import io .cdap .wrangler .proto .id .WorkspaceEntityId ;
6165import io .cdap .wrangler .proto .recipe .v2 .Recipe ;
6266import io .cdap .wrangler .proto .recipe .v2 .RecipeId ;
6367import io .cdap .wrangler .proto .workspace .v2 .Artifact ;
8589import io .cdap .wrangler .utils .SchemaConverter ;
8690import io .cdap .wrangler .utils .StructuredToRowTransformer ;
8791import org .apache .commons .lang3 .StringEscapeUtils ;
92+ import org .slf4j .Logger ;
93+ import org .slf4j .LoggerFactory ;
8894
8995import java .net .HttpURLConnection ;
9096import java .nio .charset .StandardCharsets ;
@@ -121,6 +127,8 @@ public class WorkspaceHandler extends AbstractDirectiveHandler {
121127 private WorkspaceStore wsStore ;
122128 private RecipeStore recipeStore ;
123129 private ConnectionDiscoverer discoverer ;
130+ private ContextAccessEnforcer contextAccessEnforcer ;
131+ private boolean authEnforcementEnabled ;
124132
125133 // Injected by CDAP
126134 @ SuppressWarnings ("unused" )
@@ -132,6 +140,8 @@ public void initialize(SystemHttpServiceContext context) throws Exception {
132140 wsStore = new WorkspaceStore (context );
133141 recipeStore = new RecipeStore (context );
134142 discoverer = new ConnectionDiscoverer (context );
143+ contextAccessEnforcer = context .getContextAccessEnforcer ();
144+ authEnforcementEnabled = Feature .WRANGLER_WORKSPACE_AUTH_CHECK .isEnabled (context );
135145 }
136146
137147 @ POST
@@ -144,6 +154,11 @@ public void createWorkspace(HttpServiceRequest request, HttpServiceResponder res
144154 throw new BadRequestException ("Creating workspace in system namespace is currently not supported" );
145155 }
146156
157+ WorkspaceId wsId = new WorkspaceId (ns );
158+ if (authEnforcementEnabled ) {
159+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
160+ StandardPermission .CREATE );
161+ }
147162 WorkspaceCreationRequest creationRequest =
148163 GSON .fromJson (StandardCharsets .UTF_8 .decode (request .getContent ()).toString (), WorkspaceCreationRequest .class );
149164
@@ -171,7 +186,6 @@ public void createWorkspace(HttpServiceRequest request, HttpServiceResponder res
171186 return new StageSpec (plugin .getSchema (), pluginSpec );
172187 }).collect (Collectors .toSet ()), detail .getSupportedSampleTypes (), sampleRequest );
173188
174- WorkspaceId wsId = new WorkspaceId (ns );
175189 long now = System .currentTimeMillis ();
176190 Workspace workspace = Workspace .builder (generateWorkspaceName (wsId , creationRequest .getSampleRequest ().getPath ()),
177191 wsId .getWorkspaceId ())
@@ -190,6 +204,10 @@ public void listWorkspaces(HttpServiceRequest request, HttpServiceResponder resp
190204 if (ns .getName ().equalsIgnoreCase (NamespaceId .SYSTEM .getNamespace ())) {
191205 throw new BadRequestException ("Listing workspaces in system namespace is currently not supported" );
192206 }
207+ if (authEnforcementEnabled ) {
208+ contextAccessEnforcer .enforceOnParent (EntityType .SYSTEM_APP_ENTITY , new NamespaceId (ns .getName ()),
209+ StandardPermission .LIST );
210+ }
193211 responder .sendString (GSON .toJson (new ServiceResponse <>(wsStore .listWorkspaces (ns ))));
194212 });
195213 }
@@ -204,7 +222,12 @@ public void getWorkspace(HttpServiceRequest request, HttpServiceResponder respon
204222 if (ns .getName ().equalsIgnoreCase (NamespaceId .SYSTEM .getNamespace ())) {
205223 throw new BadRequestException ("Getting workspace in system namespace is currently not supported" );
206224 }
207- responder .sendString (GSON .toJson (wsStore .getWorkspace (new WorkspaceId (ns , workspaceId ))));
225+ WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
226+ if (authEnforcementEnabled ) {
227+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
228+ StandardPermission .GET );
229+ }
230+ responder .sendString (GSON .toJson (wsStore .getWorkspace (wsId )));
208231 });
209232 }
210233
@@ -221,11 +244,14 @@ public void updateWorkspace(HttpServiceRequest request, HttpServiceResponder res
221244 if (ns .getName ().equalsIgnoreCase (NamespaceId .SYSTEM .getNamespace ())) {
222245 throw new BadRequestException ("Updating workspace in system namespace is currently not supported" );
223246 }
224-
247+ WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
248+ if (authEnforcementEnabled ) {
249+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
250+ StandardPermission .UPDATE );
251+ }
225252 WorkspaceUpdateRequest updateRequest =
226253 GSON .fromJson (StandardCharsets .UTF_8 .decode (request .getContent ()).toString (), WorkspaceUpdateRequest .class );
227254
228- WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
229255 Workspace newWorkspace = Workspace .builder (wsStore .getWorkspace (wsId ))
230256 .setDirectives (updateRequest .getDirectives ())
231257 .setInsights (updateRequest .getInsights ())
@@ -250,6 +276,10 @@ public void resampleWorkspace(HttpServiceRequest request, HttpServiceResponder r
250276 }
251277
252278 WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
279+ if (authEnforcementEnabled ) {
280+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
281+ StandardPermission .UPDATE );
282+ }
253283 Workspace currentWorkspace = wsStore .getWorkspace (wsId );
254284
255285 String connectionName = currentWorkspace .getSampleSpec () == null ? null :
@@ -294,7 +324,12 @@ public void deleteWorkspace(HttpServiceRequest request, HttpServiceResponder res
294324 if (ns .getName ().equalsIgnoreCase (NamespaceId .SYSTEM .getNamespace ())) {
295325 throw new BadRequestException ("Deleting workspace in system namespace is currently not supported" );
296326 }
297- wsStore .deleteWorkspace (new WorkspaceId (ns , workspaceId ));
327+ WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
328+ if (authEnforcementEnabled ) {
329+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
330+ StandardPermission .DELETE );
331+ }
332+ wsStore .deleteWorkspace (wsId );
298333 responder .sendStatus (HttpURLConnection .HTTP_OK );
299334 });
300335 }
@@ -312,6 +347,11 @@ public void upload(HttpServiceRequest request, HttpServiceResponder responder,
312347 throw new BadRequestException ("Uploading data in system namespace is currently not supported" );
313348 }
314349
350+ WorkspaceId id = new WorkspaceId (ns );
351+ if (authEnforcementEnabled ) {
352+ contextAccessEnforcer .enforce (new WorkspaceEntityId (id .getNamespace ().getName (), id .getWorkspaceId ()),
353+ StandardPermission .CREATE );
354+ }
315355 String name = request .getHeader (PropertyIds .FILE_NAME );
316356 if (name == null ) {
317357 throw new BadRequestException ("Name must be provided in the 'file' header" );
@@ -335,7 +375,6 @@ public void upload(HttpServiceRequest request, HttpServiceResponder responder,
335375 sample .add (new Row (COLUMN_NAME , line ));
336376 }
337377
338- WorkspaceId id = new WorkspaceId (ns );
339378 long now = System .currentTimeMillis ();
340379 Workspace workspace = Workspace .builder (name , id .getWorkspaceId ())
341380 .setCreatedTimeMillis (now ).setUpdatedTimeMillis (now ).build ();
@@ -360,9 +399,12 @@ public void execute(HttpServiceRequest request, HttpServiceResponder responder,
360399 @ PathParam ("id" ) String workspaceId ) {
361400 respond (responder , namespace , ns -> {
362401 validateNamespace (ns , "Executing directives in system namespace is currently not supported" );
363-
364- DirectiveExecutionResponse response = execute (ns , request , new WorkspaceId (ns , workspaceId ),
365- null );
402+ WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
403+ if (authEnforcementEnabled ) {
404+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
405+ StandardPermission .USE );
406+ }
407+ DirectiveExecutionResponse response = execute (ns , request , wsId , null );
366408 responder .sendJson (response );
367409 });
368410 }
@@ -406,6 +448,10 @@ public void specification(HttpServiceRequest request, HttpServiceResponder respo
406448 composite .reload (namespace );
407449
408450 WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
451+ if (authEnforcementEnabled ) {
452+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
453+ StandardPermission .GET );
454+ }
409455 WorkspaceDetail detail = wsStore .getWorkspaceDetail (wsId );
410456 List <String > directives = new ArrayList <>(detail .getWorkspace ().getDirectives ());
411457 UserDirectivesCollector userDirectivesCollector = new UserDirectivesCollector ();
@@ -448,12 +494,15 @@ public void applyRecipe(HttpServiceRequest request, HttpServiceResponder respond
448494 @ PathParam ("recipe-id" ) String recipeIdString ) {
449495 respond (responder , namespace , ns -> {
450496 validateNamespace (ns , "Executing directives in system namespace is currently not supported" );
451-
497+ WorkspaceId wsId = new WorkspaceId (ns , workspaceId );
498+ if (authEnforcementEnabled ) {
499+ contextAccessEnforcer .enforce (new WorkspaceEntityId (wsId .getNamespace ().getName (), wsId .getWorkspaceId ()),
500+ StandardPermission .USE );
501+ }
452502 RecipeId recipeId = RecipeId .builder (ns ).setRecipeId (recipeIdString ).build ();
453503 Recipe recipe = recipeStore .getRecipeById (recipeId );
454504
455- DirectiveExecutionResponse response = execute (ns , request , new WorkspaceId (ns , workspaceId ),
456- recipe .getDirectives ());
505+ DirectiveExecutionResponse response = execute (ns , request , wsId , recipe .getDirectives ());
457506 responder .sendJson (response );
458507 });
459508 }
0 commit comments