From d1cfe0d6f4310494a8b382128284e842cfe8e5e7 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Thu, 5 Feb 2026 16:26:56 -0800 Subject: [PATCH 1/6] NVSHAS-10256: Add user validation on the APIs which does not communicate with controller --- .../scala/com/neu/api/device/DeviceApi.scala | 2 +- .../api/notification/NotificationApi.scala | 8 +- .../main/scala/com/neu/api/risk/RiskApi.scala | 2 +- .../com/neu/core/AuthenticationManager.scala | 12 ++ .../authentication/SuseAuthService.scala | 7 +- .../neu/service/device/DeviceService.scala | 30 ++-- .../notification/NotificationService.scala | 132 +++++++++++++----- .../com/neu/service/risk/RiskService.scala | 23 ++- .../network-activities.component.ts | 2 +- 9 files changed, 158 insertions(+), 60 deletions(-) diff --git a/admin/src/main/scala/com/neu/api/device/DeviceApi.scala b/admin/src/main/scala/com/neu/api/device/DeviceApi.scala index 25f495b3d..517e3048c 100644 --- a/admin/src/main/scala/com/neu/api/device/DeviceApi.scala +++ b/admin/src/main/scala/com/neu/api/device/DeviceApi.scala @@ -264,7 +264,7 @@ class DeviceApi(resourceService: DeviceService) extends BaseApi { path("check") { get { Utils.respondWithWebServerHeaders() { - resourceService.checkDebugLog() + resourceService.checkDebugLog(tokenId) } } } diff --git a/admin/src/main/scala/com/neu/api/notification/NotificationApi.scala b/admin/src/main/scala/com/neu/api/notification/NotificationApi.scala index e1e5af83c..7961b9a12 100644 --- a/admin/src/main/scala/com/neu/api/notification/NotificationApi.scala +++ b/admin/src/main/scala/com/neu/api/notification/NotificationApi.scala @@ -179,7 +179,7 @@ class NotificationApi(resourceService: NotificationService) extends BaseApi { post { entity(as[UserGraphLayout]) { (graphLayout: UserGraphLayout) => Utils.respondWithWebServerHeaders() { - resourceService.createNetworkGraph(graphLayout) + resourceService.createNetworkGraph(graphLayout, tokenId) } } } @@ -188,7 +188,7 @@ class NotificationApi(resourceService: NotificationService) extends BaseApi { get { parameter(Symbol("user")) { user => Utils.respondWithWebServerHeaders() { - resourceService.getNetworkGraphLayout(user) + resourceService.getNetworkGraphLayout(user, tokenId) } } } @@ -197,14 +197,14 @@ class NotificationApi(resourceService: NotificationService) extends BaseApi { get { parameter(Symbol("user")) { user => Utils.respondWithWebServerHeaders() { - resourceService.getNetworkGraphBlacklist(user) + resourceService.getNetworkGraphBlacklist(user, tokenId) } } } ~ post { entity(as[UserBlacklist]) { (userBlacklist: UserBlacklist) => Utils.respondWithWebServerHeaders() { - resourceService.createNetworkGraphBlacklist(userBlacklist) + resourceService.createNetworkGraphBlacklist(userBlacklist, tokenId) } } } diff --git a/admin/src/main/scala/com/neu/api/risk/RiskApi.scala b/admin/src/main/scala/com/neu/api/risk/RiskApi.scala index b16cc278e..524828ca4 100644 --- a/admin/src/main/scala/com/neu/api/risk/RiskApi.scala +++ b/admin/src/main/scala/com/neu/api/risk/RiskApi.scala @@ -204,7 +204,7 @@ class RiskApi(resourceService: RiskService) extends BaseApi { path("complianceNIST") { post { entity(as[ComplianceNISTConfigData]) { complianceNISTConfigData => - resourceService.queryNistCompliances(complianceNISTConfigData) + resourceService.queryNistCompliances(complianceNISTConfigData, tokenId) } } } ~ diff --git a/admin/src/main/scala/com/neu/core/AuthenticationManager.scala b/admin/src/main/scala/com/neu/core/AuthenticationManager.scala index f7e83d7c0..c5936873e 100644 --- a/admin/src/main/scala/com/neu/core/AuthenticationManager.scala +++ b/admin/src/main/scala/com/neu/core/AuthenticationManager.scala @@ -9,6 +9,10 @@ import com.typesafe.scalalogging.LazyLogging import java.security.MessageDigest import java.text.SimpleDateFormat import scala.collection.mutable +import com.neu.client.RestClient +import com.neu.client.RestClient.* +import org.apache.pekko.http.scaladsl.model.* +import scala.concurrent.Future /** * Created by bxu on 3/25/16. @@ -109,6 +113,14 @@ object AuthenticationManager extends LazyLogging { def putToken(id: String, userToken: UserTokenNew): Unit = tokenMap += id -> userToken + def validateToken(tokenId: String): Future[HttpResponse] = + RestClient.httpRequestWithHeader( + s"${baseClusterUri(tokenId)}/$auth", + HttpMethods.PATCH, + "", + tokenId + ) + private def getRolesDigit( global_role: String, role_domains: Option[Map[String, Array[String]]] diff --git a/admin/src/main/scala/com/neu/service/authentication/SuseAuthService.scala b/admin/src/main/scala/com/neu/service/authentication/SuseAuthService.scala index 765a05ff0..57ccf3225 100644 --- a/admin/src/main/scala/com/neu/service/authentication/SuseAuthService.scala +++ b/admin/src/main/scala/com/neu/service/authentication/SuseAuthService.scala @@ -39,12 +39,7 @@ class SuseAuthService()(implicit override def validateToken(tokenId: Option[String], ip: Option[RemoteAddress]): Route = complete { - RestClient.httpRequestWithHeader( - s"${baseClusterUri(tokenId.get)}/$auth", - HttpMethods.PATCH, - "", - tokenId.get - ) + AuthenticationManager.validateToken(tokenId.get) } override def login(ip: RemoteAddress, host: String, ctx: RequestContext): Route = { diff --git a/admin/src/main/scala/com/neu/service/device/DeviceService.scala b/admin/src/main/scala/com/neu/service/device/DeviceService.scala index 681aa4978..5488aa3f1 100644 --- a/admin/src/main/scala/com/neu/service/device/DeviceService.scala +++ b/admin/src/main/scala/com/neu/service/device/DeviceService.scala @@ -628,15 +628,27 @@ class DeviceService extends Directives with DefaultJsonFormats with LazyLogging } } - def checkDebugLog(): Route = complete { - val isFileReady = Files.exists(Paths.get(logFile)) && Files.isReadable( - Paths.get(logFile) - ) - logger.info(s"Log file $logFile is ready: $isFileReady") - if (isFileReady) { - HttpResponse(StatusCodes.OK, entity = "Ready") - } else { - HttpResponse(StatusCodes.PartialContent, entity = "In progress") + def checkDebugLog(tokenId: String): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + val isFileReady = Files.exists(Paths.get(logFile)) && Files.isReadable( + Paths.get(logFile) + ) + logger.info(s"Log file $logFile is ready: $isFileReady") + if (isFileReady) { + HttpResponse(StatusCodes.OK, entity = "Ready") + } else { + HttpResponse(StatusCodes.PartialContent, entity = "In progress") + } + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) } } diff --git a/admin/src/main/scala/com/neu/service/notification/NotificationService.scala b/admin/src/main/scala/com/neu/service/notification/NotificationService.scala index 02a1ec6c6..c859006d3 100644 --- a/admin/src/main/scala/com/neu/service/notification/NotificationService.scala +++ b/admin/src/main/scala/com/neu/service/notification/NotificationService.scala @@ -5,6 +5,7 @@ import com.neu.cache.GraphCacheManager import com.neu.cache.paginationCacheManager import com.neu.client.RestClient import com.neu.client.RestClient.* +import com.neu.core.AuthenticationManager import com.neu.core.IpGeoManager import com.neu.model.AlertJsonProtocol.{ *, given } import com.neu.model.DashboardJsonProtocol.{ *, given } @@ -16,10 +17,9 @@ import com.neu.model.* import com.neu.service.BaseService import com.neu.utils.EnumUtils import com.typesafe.scalalogging.LazyLogging -import org.apache.pekko.http.scaladsl.model.HttpEntity import org.apache.pekko.http.scaladsl.model.HttpMethods.* -import org.apache.pekko.http.scaladsl.model.StatusCodes import org.apache.pekko.http.scaladsl.server.Route +import org.apache.pekko.http.scaladsl.model.* import org.joda.time.DateTime import org.json4s.* import org.json4s.native.JsonMethods.* @@ -39,8 +39,9 @@ class NotificationService()(implicit with DefaultJsonFormats with LazyLogging { - val topLimit = 5 - val client = "client" + final val serverErrorStatus = "Status: 503" + val topLimit = 5 + val client = "client" def getIpLocations(ipList: Array[String]): Route = complete { logger.info("Getting ip locations") @@ -162,27 +163,39 @@ class NotificationService()(implicit } if (start.isDefined && limit.isDefined) { - if (elements == null) { - elements = paginationCacheManager[List[org.json4s.JsonAST.JValue]] + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + if (elements == null) { + elements = paginationCacheManager[List[org.json4s.JsonAST.JValue]] + .getPagedData(s"$cacheKey-audit") + .getOrElse(List[org.json4s.JsonAST.JValue]()) + } + val output = + elements.slice(start.get.toInt, start.get.toInt + limit.get.toInt) + if (output.length < limit.get.toInt) { + paginationCacheManager[List[org.json4s.JsonAST.JValue]] + .removePagedData(s"$cacheKey-audit") + } + val pagedRes = compact(render(JArray(output))) + val cachedData = paginationCacheManager[List[org.json4s.JsonAST.JValue]] .getPagedData(s"$cacheKey-audit") .getOrElse(List[org.json4s.JsonAST.JValue]()) + logger.info("Cached data size: {}", cachedData.size) + logger.info( + "Paged response size: {}", + compact(render(JArray(output))).length + ) + pagedRes + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) } - val output = - elements.slice(start.get.toInt, start.get.toInt + limit.get.toInt) - if (output.length < limit.get.toInt) { - paginationCacheManager[List[org.json4s.JsonAST.JValue]] - .removePagedData(s"$cacheKey-audit") - } - val pagedRes = compact(render(JArray(output))) - val cachedData = paginationCacheManager[List[org.json4s.JsonAST.JValue]] - .getPagedData(s"$cacheKey-audit") - .getOrElse(List[org.json4s.JsonAST.JValue]()) - logger.info("Cached data size: {}", cachedData.size) - logger.info( - "Paged response size: {}", - compact(render(JArray(output))).length - ) - pagedRes } else { auditStr } @@ -400,26 +413,75 @@ class NotificationService()(implicit } } - def createNetworkGraph(graphLayout: UserGraphLayout): Route = { - logger.info("saving positions for user: {}", graphLayout.user) - GraphCacheManager.saveNodeLayout(graphLayout) - logger.debug(layoutToJson(graphLayout)) + def createNetworkGraph(graphLayout: UserGraphLayout, tokenId: String): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + + logger.info("saving positions for user: {}", graphLayout.user) + GraphCacheManager.saveNodeLayout(graphLayout) + logger.debug(layoutToJson(graphLayout)) - complete(HttpEntity.Empty) + HttpEntity.Empty + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) + } } - def getNetworkGraphLayout(user: String): Route = complete { - UserGraphLayout(user, GraphCacheManager.getNodeLayout(user)) + def getNetworkGraphLayout(user: String, tokenId: String): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + UserGraphLayout(user, GraphCacheManager.getNodeLayout(user)) + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) + } } - def getNetworkGraphBlacklist(user: String): Route = complete { - BlacklistCacheManager.getBlacklist(user) + def getNetworkGraphBlacklist(user: String, tokenId: String): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + BlacklistCacheManager.getBlacklist(user) + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) + } } - def createNetworkGraphBlacklist(userBlacklist: UserBlacklist): Route = { - logger.info("saving blacklist for user: {}", userBlacklist.user) - BlacklistCacheManager.saveBlacklist(userBlacklist) - complete(HttpEntity.Empty) + def createNetworkGraphBlacklist(userBlacklist: UserBlacklist, tokenId: String): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + logger.info("saving blacklist for user: {}", userBlacklist.user) + BlacklistCacheManager.saveBlacklist(userBlacklist) + HttpEntity.Empty + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) + } } def getSecurityEvents(tokenId: String): Route = complete { diff --git a/admin/src/main/scala/com/neu/service/risk/RiskService.scala b/admin/src/main/scala/com/neu/service/risk/RiskService.scala index aaded96b5..dfece0859 100644 --- a/admin/src/main/scala/com/neu/service/risk/RiskService.scala +++ b/admin/src/main/scala/com/neu/service/risk/RiskService.scala @@ -20,6 +20,8 @@ import scala.concurrent.TimeoutException import scala.util.control.NonFatal import java.net.URLEncoder import java.nio.charset.StandardCharsets +import scala.concurrent.Await +import scala.concurrent.duration.DurationInt class RiskService extends BaseService with DefaultJsonFormats with LazyLogging { @@ -317,9 +319,24 @@ class RiskService extends BaseService with DefaultJsonFormats with LazyLogging { } } - def queryNistCompliances(complianceNISTConfigData: ComplianceNISTConfigData): Route = complete { - logger.info("Get NIST compliances: {}", complianceNISTConfigData.config.names) - CisNISTManager.getCompliancesNIST(complianceNISTConfigData.config.names) + def queryNistCompliances( + complianceNISTConfigData: ComplianceNISTConfigData, + tokenId: String + ): Route = complete { + try { + val resultPromise = AuthenticationManager.validateToken(tokenId) + Await.result(resultPromise, RestClient.waitingLimit.seconds) + logger.info("Get NIST compliances: {}", complianceNISTConfigData.config.names) + CisNISTManager.getCompliancesNIST(complianceNISTConfigData.config.names) + } catch { + case NonFatal(e) => + RestClient.handleError( + timeOutStatus, + authenticationFailedStatus, + serverErrorStatus, + e + ) + } } def getCompliances(tokenId: String): Route = complete { diff --git a/admin/webapp/websrc/app/routes/network-activities/network-activities.component.ts b/admin/webapp/websrc/app/routes/network-activities/network-activities.component.ts index 8f944247b..3631d3638 100644 --- a/admin/webapp/websrc/app/routes/network-activities/network-activities.component.ts +++ b/admin/webapp/websrc/app/routes/network-activities/network-activities.component.ts @@ -225,7 +225,7 @@ export class NetworkActivitiesComponent implements OnInit, OnDestroy { gpuEnabled = JSON.parse(this.localStorage.get('_gpuEnabled')); if (gpuEnabled !== null) return gpuEnabled; } - return false + return false; } private prepareGraphics( From 39c725c47b0c1723999a3f4ca04f50224de6fabf Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Wed, 25 Feb 2026 09:19:26 -0800 Subject: [PATCH 2/6] NVSHAS-10256: Add user validation on the APIs which does not communicate with controller (PR review fix) --- admin/src/main/scala/com/neu/service/device/DeviceService.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/admin/src/main/scala/com/neu/service/device/DeviceService.scala b/admin/src/main/scala/com/neu/service/device/DeviceService.scala index 5488aa3f1..f736e386c 100644 --- a/admin/src/main/scala/com/neu/service/device/DeviceService.scala +++ b/admin/src/main/scala/com/neu/service/device/DeviceService.scala @@ -635,7 +635,7 @@ class DeviceService extends Directives with DefaultJsonFormats with LazyLogging val isFileReady = Files.exists(Paths.get(logFile)) && Files.isReadable( Paths.get(logFile) ) - logger.info(s"Log file $logFile is ready: $isFileReady") + logger.info(s"Log file $logFile is ready: $isFileReady") if (isFileReady) { HttpResponse(StatusCodes.OK, entity = "Ready") } else { From 5bf32c414ae0cdb169b20de3efa0b91e72f402de Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Tue, 3 Mar 2026 15:00:19 -0800 Subject: [PATCH 3/6] NVSHAS-10256: Add user validation on the APIs which does not communicate with controller (Fixed issue for validating username after token is valiated) --- .../com/neu/service/device/DeviceService.scala | 18 +++++++++++------- .../notification/NotificationService.scala | 12 ++++++------ .../com/neu/cache/BlacklistCacheManager.scala | 11 ++++++++--- .../com/neu/cache/GraphCacheManager.scala | 8 +++++--- 4 files changed, 30 insertions(+), 19 deletions(-) diff --git a/admin/src/main/scala/com/neu/service/device/DeviceService.scala b/admin/src/main/scala/com/neu/service/device/DeviceService.scala index f736e386c..87756baf9 100644 --- a/admin/src/main/scala/com/neu/service/device/DeviceService.scala +++ b/admin/src/main/scala/com/neu/service/device/DeviceService.scala @@ -632,14 +632,18 @@ class DeviceService extends Directives with DefaultJsonFormats with LazyLogging try { val resultPromise = AuthenticationManager.validateToken(tokenId) Await.result(resultPromise, RestClient.waitingLimit.seconds) - val isFileReady = Files.exists(Paths.get(logFile)) && Files.isReadable( - Paths.get(logFile) - ) - logger.info(s"Log file $logFile is ready: $isFileReady") - if (isFileReady) { - HttpResponse(StatusCodes.OK, entity = "Ready") + if (SupportLogAuthCacheManager.getSupportLogAuth(tokenId).isDefined) { + val isFileReady = Files.exists(Paths.get(logFile)) && Files.isReadable( + Paths.get(logFile) + ) + logger.info(s"Log file $logFile is ready: $isFileReady") + if (isFileReady) { + HttpResponse(StatusCodes.OK, entity = "Ready") + } else { + HttpResponse(StatusCodes.PartialContent, entity = "In progress") + } } else { - HttpResponse(StatusCodes.PartialContent, entity = "In progress") + (StatusCodes.Forbidden, "File can not be accessed.") } } catch { case NonFatal(e) => diff --git a/admin/src/main/scala/com/neu/service/notification/NotificationService.scala b/admin/src/main/scala/com/neu/service/notification/NotificationService.scala index c859006d3..e0794b44a 100644 --- a/admin/src/main/scala/com/neu/service/notification/NotificationService.scala +++ b/admin/src/main/scala/com/neu/service/notification/NotificationService.scala @@ -395,12 +395,12 @@ class NotificationService()(implicit val (edges: Array[Edge], markedNodes: Array[Node]) = getDataSet(graphData) logger.info("Sending data") - logger.info("blacklist: {}", BlacklistCacheManager.getBlacklist(user)) + logger.info("blacklist: {}", BlacklistCacheManager.getBlacklist(user, tokenId)) NetworkGraph( markedNodes, edges, - BlacklistCacheManager.getBlacklist(user), + BlacklistCacheManager.getBlacklist(user, tokenId), enableGPU == "true" ) @@ -419,7 +419,7 @@ class NotificationService()(implicit Await.result(resultPromise, RestClient.waitingLimit.seconds) logger.info("saving positions for user: {}", graphLayout.user) - GraphCacheManager.saveNodeLayout(graphLayout) + GraphCacheManager.saveNodeLayout(graphLayout, tokenId) logger.debug(layoutToJson(graphLayout)) HttpEntity.Empty @@ -438,7 +438,7 @@ class NotificationService()(implicit try { val resultPromise = AuthenticationManager.validateToken(tokenId) Await.result(resultPromise, RestClient.waitingLimit.seconds) - UserGraphLayout(user, GraphCacheManager.getNodeLayout(user)) + UserGraphLayout(user, GraphCacheManager.getNodeLayout(user, tokenId)) } catch { case NonFatal(e) => RestClient.handleError( @@ -454,7 +454,7 @@ class NotificationService()(implicit try { val resultPromise = AuthenticationManager.validateToken(tokenId) Await.result(resultPromise, RestClient.waitingLimit.seconds) - BlacklistCacheManager.getBlacklist(user) + BlacklistCacheManager.getBlacklist(user, tokenId) } catch { case NonFatal(e) => RestClient.handleError( @@ -471,7 +471,7 @@ class NotificationService()(implicit val resultPromise = AuthenticationManager.validateToken(tokenId) Await.result(resultPromise, RestClient.waitingLimit.seconds) logger.info("saving blacklist for user: {}", userBlacklist.user) - BlacklistCacheManager.saveBlacklist(userBlacklist) + BlacklistCacheManager.saveBlacklist(userBlacklist, tokenId) HttpEntity.Empty } catch { case NonFatal(e) => diff --git a/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala b/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala index ad01bd352..26f57710d 100644 --- a/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala +++ b/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala @@ -16,15 +16,20 @@ object BlacklistCacheManager { /** * Save blacklist for Graph. */ - def saveBlacklist(userBlacklist: UserBlacklist): Unit = - userBlacklist.blacklist.foreach(cache.put(userBlacklist.user + "blacklist", _)) + def saveBlacklist(userBlacklist: UserBlacklist, tokenId: String): Unit = + userBlacklist.blacklist.foreach( + cache.put(userBlacklist.user + tokenId.substring(0, 20) + "blacklist", _) + ) /** * Get blacklist of user for Graph * @param user * the user + * @param tokenId + * the token ID * @return * [[com.neu.model.Blacklist]] */ - def getBlacklist(user: String): Option[Blacklist] = cache.get(user + "blacklist") + def getBlacklist(user: String, tokenId: String): Option[Blacklist] = + cache.get(user + tokenId.substring(0, 20) + "blacklist") } diff --git a/common/src/main/scala/com/neu/cache/GraphCacheManager.scala b/common/src/main/scala/com/neu/cache/GraphCacheManager.scala index 653e4994d..f6c8b5231 100644 --- a/common/src/main/scala/com/neu/cache/GraphCacheManager.scala +++ b/common/src/main/scala/com/neu/cache/GraphCacheManager.scala @@ -23,8 +23,9 @@ object GraphCacheManager { * @param layout * the [[com.neu.model.UserGraphLayout]] */ - def saveNodeLayout(layout: UserGraphLayout): Unit = - if (layout.nodePositions.nonEmpty) cache.put(layout.user + "node", layout.nodePositions.get) + def saveNodeLayout(layout: UserGraphLayout, tokenId: String): Unit = + if (layout.nodePositions.nonEmpty) + cache.put(layout.user + tokenId.substring(0, 20) + "node", layout.nodePositions.get) /** * Get node graph layout for user @@ -33,7 +34,8 @@ object GraphCacheManager { * @return * [[com.neu.model.Position]] */ - def getNodeLayout(user: String): Option[Map[String, Position]] = cache.get(user + "node") + def getNodeLayout(user: String, tokenId: String): Option[Map[String, Position]] = + cache.get(user + tokenId.substring(0, 20) + "node") /** * Get group layout for user From 90da319deac6976eff6a87b1b2797aba586c1c2b Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Mon, 9 Mar 2026 13:49:07 -0700 Subject: [PATCH 4/6] NVSHAS-10256: Add user validation on the APIs which does not communicate with controller (Optimized algorithm for shortening token in the cache key) --- .../com/neu/cache/BlacklistCacheManager.scala | 5 +++-- .../scala/com/neu/cache/GraphCacheManager.scala | 5 +++-- common/src/main/scala/com/neu/utils/Common.scala | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) create mode 100644 common/src/main/scala/com/neu/utils/Common.scala diff --git a/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala b/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala index 26f57710d..824a8c78c 100644 --- a/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala +++ b/common/src/main/scala/com/neu/cache/BlacklistCacheManager.scala @@ -3,6 +3,7 @@ package com.neu.cache import com.neu.model.Blacklist import com.neu.model.UserBlacklist import net.sf.ehcache.CacheManager +import com.neu.utils.Common.shortKey object BlacklistCacheManager { given cacheKeyGenerator: ToStringCacheKeyGenerator.type = ToStringCacheKeyGenerator @@ -18,7 +19,7 @@ object BlacklistCacheManager { */ def saveBlacklist(userBlacklist: UserBlacklist, tokenId: String): Unit = userBlacklist.blacklist.foreach( - cache.put(userBlacklist.user + tokenId.substring(0, 20) + "blacklist", _) + cache.put(userBlacklist.user + shortKey(tokenId) + "blacklist", _) ) /** @@ -31,5 +32,5 @@ object BlacklistCacheManager { * [[com.neu.model.Blacklist]] */ def getBlacklist(user: String, tokenId: String): Option[Blacklist] = - cache.get(user + tokenId.substring(0, 20) + "blacklist") + cache.get(user + shortKey(tokenId) + "blacklist") } diff --git a/common/src/main/scala/com/neu/cache/GraphCacheManager.scala b/common/src/main/scala/com/neu/cache/GraphCacheManager.scala index f6c8b5231..b59c2e789 100644 --- a/common/src/main/scala/com/neu/cache/GraphCacheManager.scala +++ b/common/src/main/scala/com/neu/cache/GraphCacheManager.scala @@ -3,6 +3,7 @@ package com.neu.cache import com.neu.model.Position import com.neu.model.UserGraphLayout import net.sf.ehcache.CacheManager +import com.neu.utils.Common.shortKey /** * Created by bxu on 2/2/18. Manager graph layout for node and group view. @@ -25,7 +26,7 @@ object GraphCacheManager { */ def saveNodeLayout(layout: UserGraphLayout, tokenId: String): Unit = if (layout.nodePositions.nonEmpty) - cache.put(layout.user + tokenId.substring(0, 20) + "node", layout.nodePositions.get) + cache.put(layout.user + shortKey(tokenId) + "node", layout.nodePositions.get) /** * Get node graph layout for user @@ -35,7 +36,7 @@ object GraphCacheManager { * [[com.neu.model.Position]] */ def getNodeLayout(user: String, tokenId: String): Option[Map[String, Position]] = - cache.get(user + tokenId.substring(0, 20) + "node") + cache.get(user + shortKey(tokenId) + "node") /** * Get group layout for user diff --git a/common/src/main/scala/com/neu/utils/Common.scala b/common/src/main/scala/com/neu/utils/Common.scala new file mode 100644 index 000000000..1352987e8 --- /dev/null +++ b/common/src/main/scala/com/neu/utils/Common.scala @@ -0,0 +1,14 @@ +package com.neu.utils + +import java.security.MessageDigest + +object Common { + def shortKey(jwt: String): String = { + val digest = MessageDigest.getInstance("SHA-256") + digest + .digest(jwt.getBytes("UTF-8")) + .take(20) + .map("%02x".format(_)) + .mkString + } +} From 3e705c8a849d0a162807aaed446533d54f5e8851 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Wed, 11 Mar 2026 18:13:39 -0700 Subject: [PATCH 5/6] NVSHAS-10256: Add user validation on the APIs which does not communicate with controller (Fixed PR review issues) --- common/src/main/scala/com/neu/utils/Common.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/src/main/scala/com/neu/utils/Common.scala b/common/src/main/scala/com/neu/utils/Common.scala index 1352987e8..ed98d01b4 100644 --- a/common/src/main/scala/com/neu/utils/Common.scala +++ b/common/src/main/scala/com/neu/utils/Common.scala @@ -6,8 +6,8 @@ object Common { def shortKey(jwt: String): String = { val digest = MessageDigest.getInstance("SHA-256") digest - .digest(jwt.getBytes("UTF-8")) - .take(20) + .digest(jwt.getBytes(java.nio.charset.StandardCharsets.UTF_8)) + .take(10) .map("%02x".format(_)) .mkString } From ad1773235ebbbdf4a7337e1fa730d36e58de3633 Mon Sep 17 00:00:00 2001 From: Steven Zhang Date: Fri, 13 Mar 2026 13:32:28 -0700 Subject: [PATCH 6/6] #1150 [UI] Layout and alignment after Angular 20 upgrade (Score gauge & Namespace list) --- ...-improvement-completed-view.component.html | 6 ++-- .../report-by-namespace-modal.component.html | 31 ++++++++++--------- .../report-by-namespace-modal.component.scss | 11 +++++++ .../report-by-namespace-modal.component.ts | 4 +++ 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/admin/webapp/websrc/app/routes/components/score-improvement-modal/partial/score-improvement-completed-view/score-improvement-completed-view.component.html b/admin/webapp/websrc/app/routes/components/score-improvement-modal/partial/score-improvement-completed-view/score-improvement-completed-view.component.html index a03f71981..fa143ccb3 100644 --- a/admin/webapp/websrc/app/routes/components/score-improvement-modal/partial/score-improvement-completed-view/score-improvement-completed-view.component.html +++ b/admin/webapp/websrc/app/routes/components/score-improvement-modal/partial/score-improvement-completed-view/score-improvement-completed-view.component.html @@ -12,8 +12,6 @@ [foregroundColor]="gaugeColor" [thick]="10" cap="round" - [thick]="10" - cap="round" [size]="120">
@@ -35,7 +33,9 @@ [animate]="true" [duration]="1" [foregroundColor]="gaugeLabelColorFixed" - [size]="40"> + [thick]="10" + cap="round" + [size]="120">
{{ gaugeLabelFixed }} diff --git a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.html b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.html index 71c871e3f..c593c394c 100644 --- a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.html +++ b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.html @@ -19,20 +19,23 @@ close
-
-
- - - +
+ +
- + [matTooltip]="domain" + [matTooltipDisabled]="!isOverflow(domainTextEl)" + matTooltipPosition="above"> + + {{ domain }} + +
diff --git a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.scss b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.scss index e69de29bb..54e5b320b 100644 --- a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.scss +++ b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.scss @@ -0,0 +1,11 @@ +.text-overflow-ellipsis { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.text-ns { + color: var(--mat-dialog-subhead-color, var(--mat-sys-on-surface, rgba(0, 0, 0, 0.87))) !important; + font-weight: 400 !important; + font-family: var(--mat-dialog-subhead-font, var(--mat-font-family, Roboto, "Helvetica Neue", sans-serif)) !important; +} \ No newline at end of file diff --git a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.ts b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.ts index 495412562..805d37a5f 100644 --- a/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.ts +++ b/admin/webapp/websrc/app/routes/dashboard/report-by-namespace-modal/report-by-namespace-modal.component.ts @@ -50,4 +50,8 @@ export class ReportByNamespaceModalComponent { } ); }; + + isOverflow(el: HTMLElement): boolean { + return el.scrollWidth > el.clientWidth; + } }