bobbai00 commented on code in PR #3598:
URL: https://github.com/apache/texera/pull/3598#discussion_r2343036458


##########
core/access-control-service/src/main/scala/edu/uci/ics/texera/service/access/EndpointAccessController.scala:
##########
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package edu.uci.ics.texera.service.access
+
+import com.typesafe.scalalogging.LazyLogging
+import edu.uci.ics.texera.auth.JwtParser.parseToken
+import edu.uci.ics.texera.auth.SessionUser
+import edu.uci.ics.texera.auth.util.{ComputingUnitAccess, HeaderField}
+import edu.uci.ics.texera.dao.jooq.generated.enums.PrivilegeEnum
+import jakarta.ws.rs.core.{HttpHeaders, Response, UriInfo}
+
+import java.util.Optional
+import scala.jdk.CollectionConverters.{CollectionHasAsScala, MapHasAsScala}
+import scala.util.matching.Regex
+
+class EndpointAccessController extends Authorizer with LazyLogging {

Review Comment:
   If this class is going to be generalized to all access check, not just 
computing unit access, then `Authorizer` trait is not needed as this class will 
be the only place for the access check logic



##########
core/access-control-service/src/main/scala/edu/uci/ics/texera/service/access/EndpointAccessController.scala:
##########
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package edu.uci.ics.texera.service.access
+
+import com.typesafe.scalalogging.LazyLogging
+import edu.uci.ics.texera.auth.JwtParser.parseToken
+import edu.uci.ics.texera.auth.SessionUser
+import edu.uci.ics.texera.auth.util.{ComputingUnitAccess, HeaderField}
+import edu.uci.ics.texera.dao.jooq.generated.enums.PrivilegeEnum
+import jakarta.ws.rs.core.{HttpHeaders, Response, UriInfo}
+
+import java.util.Optional
+import scala.jdk.CollectionConverters.{CollectionHasAsScala, MapHasAsScala}
+import scala.util.matching.Regex
+
+class EndpointAccessController extends Authorizer with LazyLogging {
+
+  private val computingUnitAccess: ComputingUnitAccess = new 
ComputingUnitAccess()
+
+  // Regex for the paths that require authorization
+  private val wsapiWorkflowWebsocket: Regex = 
""".*/wsapi/workflow-websocket.*""".r
+  private val apiExecutionsStats: Regex = 
""".*/api/executions/[0-9]+/stats/[0-9]+.*""".r
+  private val apiExecutionsResultExport: Regex = 
""".*/api/executions/result/export.*""".r
+
+  override def authorize(uriInfo: UriInfo, headers: HttpHeaders): Response = {
+    val path = uriInfo.getPath
+    logger.info(s"Authorizing request for path: $path")
+
+    path match {
+      case wsapiWorkflowWebsocket()        => performAuth(uriInfo, headers)

Review Comment:
   since three cases are the same, you can merge them into one line



##########
core/amber/src/main/scala/edu/uci/ics/texera/web/ServletAwareConfigurator.scala:
##########
@@ -29,46 +32,85 @@ import java.nio.charset.Charset
 import javax.websocket.HandshakeResponse
 import javax.websocket.server.{HandshakeRequest, ServerEndpointConfig}
 import scala.jdk.CollectionConverters.ListHasAsScala
+import scala.jdk.CollectionConverters._
 
 /**
-  * This configurator extracts HTTPSession and associates it to 
ServerEndpointConfig,
-  * allow it to be accessed by Websocket connections.
-  * <pre>
-  * See <a 
href="https://stackoverflow.com/questions/17936440/accessing-httpsession-
-  * from-httpservletrequest-in-a-web-socket-serverendpoint"></a>
-  * </pre>
-  */
+ * This configurator extracts HTTPSession and associates it to 
ServerEndpointConfig,
+ * allow it to be accessed by Websocket connections.
+ * <pre>
+ * See <a 
href="https://stackoverflow.com/questions/17936440/accessing-httpsession-
+ * from-httpservletrequest-in-a-web-socket-serverendpoint"></a>
+ * </pre>
+ */
 class ServletAwareConfigurator extends ServerEndpointConfig.Configurator with 
LazyLogging {
 
   override def modifyHandshake(
-      config: ServerEndpointConfig,
-      request: HandshakeRequest,
-      response: HandshakeResponse
-  ): Unit = {
+                                config: ServerEndpointConfig,
+                                request: HandshakeRequest,
+                                response: HandshakeResponse
+                              ): Unit = {
     try {
-      val params =
-        URLEncodedUtils.parse(new URI("?" + request.getQueryString), 
Charset.defaultCharset())
-      params.asScala
-        .map(pair => pair.getName -> pair.getValue)
-        .toMap
-        .get("access-token")
-        .map(token => {
-          val claims = jwtConsumer.process(token).getJwtClaims
-          config.getUserProperties.put(
-            classOf[User].getName,
-            new User(
-              claims.getClaimValue("userId").asInstanceOf[Long].toInt,
-              claims.getSubject,
-              
String.valueOf(claims.getClaimValue("email").asInstanceOf[String]),
-              null,
-              null,
-              null,
-              null,
-              null
-            )
+      val headers = 
request.getHeaders.asScala.view.mapValues(_.asScala.headOption).toMap
+      if (
+        headers.contains(HeaderField.UserComputingUnitAccess) &&
+        headers.contains(HeaderField.UserId) &&
+        headers.contains(HeaderField.UserName) &&
+        headers.contains(HeaderField.UserEmail)
+      ) {
+        // Already authorized by Access Control Service
+
+        val userId = headers.get(HeaderField.UserId).flatten.map(_.toInt).get
+        val userName = headers.get(HeaderField.UserName).flatten.get
+        val userEmail = headers.get(HeaderField.UserEmail).flatten.get
+        val cuAccess = 
headers.get(HeaderField.UserComputingUnitAccess).flatten.getOrElse("")
+        config.getUserProperties.put(HeaderField.UserComputingUnitAccess, 
cuAccess)
+        logger.info(
+          s"User ID: $userId, User Name: $userName, User Email: $userEmail 
with CU Access: $cuAccess"
+        )
+
+        config.getUserProperties.put(
+          classOf[User].getName,
+          new User(
+            userId,
+            userName,
+            userEmail,
+            null,
+            null,
+            null,
+            null,
+            null
           )
-        })
+        )
+        logger.debug(s"User created from headers: ID=$userId, Name=$userName")
+
+      } else {
+        // STANDALONE MODE: Construct the User object from JWT in query 
parameters.

Review Comment:
   better to call it single-node mode. And later we should drop this `else` 
logic once we have a required access service across both modes (single-node and 
cluster)



##########
core/access-control-service/src/main/scala/edu/uci/ics/texera/service/access/EndpointAccessController.scala:
##########
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package edu.uci.ics.texera.service.access
+
+import com.typesafe.scalalogging.LazyLogging
+import edu.uci.ics.texera.auth.JwtParser.parseToken
+import edu.uci.ics.texera.auth.SessionUser
+import edu.uci.ics.texera.auth.util.{ComputingUnitAccess, HeaderField}
+import edu.uci.ics.texera.dao.jooq.generated.enums.PrivilegeEnum
+import jakarta.ws.rs.core.{HttpHeaders, Response, UriInfo}
+
+import java.util.Optional
+import scala.jdk.CollectionConverters.{CollectionHasAsScala, MapHasAsScala}
+import scala.util.matching.Regex
+
+class EndpointAccessController extends Authorizer with LazyLogging {

Review Comment:
   Rename to `AccessChecker`



##########
core/access-control-service/src/main/scala/edu/uci/ics/texera/service/access/Authorizer.scala:
##########
@@ -0,0 +1,24 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package edu.uci.ics.texera.service.access
+
+import jakarta.ws.rs.core.{HttpHeaders, Response, UriInfo}
+
+trait Authorizer {

Review Comment:
   please add few comments to talk about the purpose of this abstraction



##########
core/access-control-service/src/main/scala/edu/uci/ics/texera/service/access/EndpointAccessController.scala:
##########
@@ -0,0 +1,105 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package edu.uci.ics.texera.service.access
+
+import com.typesafe.scalalogging.LazyLogging
+import edu.uci.ics.texera.auth.JwtParser.parseToken
+import edu.uci.ics.texera.auth.SessionUser
+import edu.uci.ics.texera.auth.util.{ComputingUnitAccess, HeaderField}
+import edu.uci.ics.texera.dao.jooq.generated.enums.PrivilegeEnum
+import jakarta.ws.rs.core.{HttpHeaders, Response, UriInfo}
+
+import java.util.Optional
+import scala.jdk.CollectionConverters.{CollectionHasAsScala, MapHasAsScala}
+import scala.util.matching.Regex
+
+class EndpointAccessController extends Authorizer with LazyLogging {
+
+  private val computingUnitAccess: ComputingUnitAccess = new 
ComputingUnitAccess()
+
+  // Regex for the paths that require authorization
+  private val wsapiWorkflowWebsocket: Regex = 
""".*/wsapi/workflow-websocket.*""".r
+  private val apiExecutionsStats: Regex = 
""".*/api/executions/[0-9]+/stats/[0-9]+.*""".r
+  private val apiExecutionsResultExport: Regex = 
""".*/api/executions/result/export.*""".r
+
+  override def authorize(uriInfo: UriInfo, headers: HttpHeaders): Response = {
+    val path = uriInfo.getPath
+    logger.info(s"Authorizing request for path: $path")
+
+    path match {
+      case wsapiWorkflowWebsocket()        => performAuth(uriInfo, headers)
+      case apiExecutionsStats()          => performAuth(uriInfo, headers)
+      case apiExecutionsResultExport()   => performAuth(uriInfo, headers)
+      case _ =>
+        logger.warn(s"No authorization logic for path: $path. Denying access.")
+        Response.status(Response.Status.FORBIDDEN).build()
+    }
+  }
+
+  private def performAuth(uriInfo: UriInfo, headers: HttpHeaders): Response = {

Review Comment:
   rename to `checkComputingUnitAccess`



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to