Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@
package edu.uci.ics.texera.web.resource.auth

import edu.uci.ics.texera.auth.JwtAuth.{
TOKEN_EXPIRE_TIME_IN_DAYS,
dayToMin,
TOKEN_EXPIRE_TIME_IN_MINUTES,
jwtClaims,
jwtConsumer,
jwtToken
Expand Down Expand Up @@ -103,7 +102,7 @@ class AuthResource {
throw new NotAcceptableException("User System is disabled on the backend!")
retrieveUserByUsernameAndPassword(request.username, request.password) match {
case Some(user) =>
TokenIssueResponse(jwtToken(jwtClaims(user, dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS))))
TokenIssueResponse(jwtToken(jwtClaims(user, TOKEN_EXPIRE_TIME_IN_MINUTES)))
case None => throw new NotAuthorizedException("Login credentials are incorrect.")
}
}
Expand All @@ -112,7 +111,7 @@ class AuthResource {
@Path("/refresh")
def refresh(request: RefreshTokenRequest): TokenIssueResponse = {
val claims = jwtConsumer.process(request.accessToken).getJwtClaims
claims.setExpirationTimeMinutesInTheFuture(dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS).toFloat)
claims.setExpirationTimeMinutesInTheFuture(TOKEN_EXPIRE_TIME_IN_MINUTES.toFloat)
TokenIssueResponse(jwtToken(claims))
}

Expand All @@ -133,7 +132,7 @@ class AuthResource {
// hash the plain text password
user.setPassword(new StrongPasswordEncryptor().encryptPassword(request.password))
userDao.insert(user)
TokenIssueResponse(jwtToken(jwtClaims(user, dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS))))
TokenIssueResponse(jwtToken(jwtClaims(user, TOKEN_EXPIRE_TIME_IN_MINUTES)))
case _ =>
// the username exists already
throw new NotAcceptableException("Username exists already.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.gson.GsonFactory
import edu.uci.ics.texera.config.UserSystemConfig
import edu.uci.ics.texera.dao.SqlServer
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_DAYS, dayToMin, jwtClaims, jwtToken}
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_MINUTES, jwtClaims, jwtToken}
import edu.uci.ics.texera.web.model.http.response.TokenIssueResponse
import edu.uci.ics.texera.dao.jooq.generated.enums.UserRoleEnum
import edu.uci.ics.texera.dao.jooq.generated.tables.daos.UserDao
Expand Down Expand Up @@ -111,7 +111,7 @@ class GoogleAuthResource {
user
}
}
TokenIssueResponse(jwtToken(jwtClaims(user, dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS))))
TokenIssueResponse(jwtToken(jwtClaims(user, TOKEN_EXPIRE_TIME_IN_MINUTES)))
} else throw new NotAuthorizedException("Login credentials are incorrect.")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,25 @@ import edu.uci.ics.texera.web.resource.GmailResource.sendEmail
import edu.uci.ics.texera.web.resource.dashboard.admin.user.AdminUserResource.userDao
import edu.uci.ics.texera.web.resource.dashboard.user.quota.UserQuotaResource._
import org.jasypt.util.password.StrongPasswordEncryptor
import edu.uci.ics.texera.dao.jooq.generated.tables.User.USER
import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog.TIME_LOG

import java.util
import javax.annotation.security.RolesAllowed
import javax.ws.rs._
import javax.ws.rs.core.{MediaType, Response}

case class UserWithLastLogin(
uid: Int,
name: String,
email: String,
googleId: String,
role: UserRoleEnum,
googleAvatar: String,
comment: String,
lastLogin: java.time.OffsetDateTime // will be null if never logged in
)

object AdminUserResource {
final private lazy val context = SqlServer
.getInstance()
Expand All @@ -57,6 +70,32 @@ class AdminUserResource {
userDao.fetchRangeOfUid(Integer.MIN_VALUE, Integer.MAX_VALUE)
}

/**
* This method returns the list of users with lastLogin time
*
* @return a list of UserWithLastLogin
*/
@GET
@Path("/listWithActivity")
@Produces(Array(MediaType.APPLICATION_JSON))
def listUserWithActivity(): util.List[UserWithLastLogin] = {
AdminUserResource.context
.select(
USER.UID,
USER.NAME,
USER.EMAIL,
USER.GOOGLE_ID,
USER.ROLE,
USER.GOOGLE_AVATAR,
USER.COMMENT,
TIME_LOG.LAST_LOGIN
)
.from(USER)
.leftJoin(TIME_LOG)
.on(USER.UID.eq(TIME_LOG.UID))
.fetchInto(classOf[UserWithLastLogin])
}

@PUT
@Path("/update")
def updateUser(user: User): Unit = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import edu.uci.ics.amber.core.virtualidentity.{OperatorIdentity, WorkflowIdentit
import edu.uci.ics.amber.core.workflow.PortIdentity
import edu.uci.ics.amber.util.ArrowUtils
import edu.uci.ics.texera.auth.JwtAuth
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_DAYS, dayToMin, jwtClaims}
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_MINUTES, jwtClaims}
import edu.uci.ics.texera.dao.jooq.generated.tables.pojos.User
import edu.uci.ics.texera.web.model.http.request.result.{OperatorExportInfo, ResultExportRequest}
import edu.uci.ics.texera.web.model.http.response.result.ResultExportResponse
Expand Down Expand Up @@ -516,7 +516,7 @@ class ResultExportService(workflowIdentity: WorkflowIdentity, computingUnitId: I
connection.setRequestProperty("Content-Type", "application/octet-stream")
connection.setRequestProperty(
"Authorization",
s"Bearer ${JwtAuth.jwtToken(jwtClaims(user, dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS)))}"
s"Bearer ${JwtAuth.jwtToken(jwtClaims(user, TOKEN_EXPIRE_TIME_IN_MINUTES))}"
)
connection.setChunkedStreamingMode(0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ import java.nio.charset.StandardCharsets
// TODO: move this logic to Auth
object JwtAuth {

final val TOKEN_EXPIRE_TIME_IN_DAYS = AuthConfig.jwtExpirationDays
final val TOKEN_SECRET: String = AuthConfig.jwtSecretKey
final val TOKEN_EXPIRE_TIME_IN_MINUTES: Int = 15

val jwtConsumer: JwtConsumer = new JwtConsumerBuilder()
.setAllowedClockSkewInSeconds(30)
Expand All @@ -59,11 +59,7 @@ object JwtAuth {
claims.setClaim("email", user.getEmail)
claims.setClaim("role", user.getRole)
claims.setClaim("googleAvatar", user.getGoogleAvatar)
claims.setExpirationTimeMinutesInTheFuture(dayToMin(expireInDays).toFloat)
claims.setExpirationTimeMinutesInTheFuture(TOKEN_EXPIRE_TIME_IN_MINUTES.toFloat)
claims
}

def dayToMin(days: Int): Int = {
days * 24 * 60
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,21 @@
package edu.uci.ics.texera.auth

import com.typesafe.scalalogging.LazyLogging
import edu.uci.ics.texera.dao.SqlServer
import edu.uci.ics.texera.dao.jooq.generated.enums.UserRoleEnum
import jakarta.ws.rs.container.{ContainerRequestContext, ContainerRequestFilter, ResourceInfo}
import jakarta.ws.rs.core.{Context, HttpHeaders, SecurityContext}
import jakarta.ws.rs.ext.Provider

import edu.uci.ics.texera.dao.jooq.generated.Tables.TIME_LOG
import java.time.OffsetDateTime
import java.security.Principal

@Provider
class JwtAuthFilter extends ContainerRequestFilter with LazyLogging {

@Context
private var resourceInfo: ResourceInfo = _
private val ctx = SqlServer.getInstance().createDSLContext()

override def filter(requestContext: ContainerRequestContext): Unit = {
val authHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION)
Expand All @@ -42,6 +45,16 @@ class JwtAuthFilter extends ContainerRequestFilter with LazyLogging {

if (userOpt.isPresent) {
val user = userOpt.get()

ctx
.insertInto(TIME_LOG)
.set(TIME_LOG.UID, user.getUid)
.set(TIME_LOG.LAST_LOGIN, OffsetDateTime.now())
.onConflict(TIME_LOG.UID) // conflict on primary key uid
.doUpdate()
.set(TIME_LOG.LAST_LOGIN, OffsetDateTime.now())
.execute()

requestContext.setSecurityContext(new SecurityContext {
override def getUserPrincipal: Principal = user
override def isUserInRole(role: String): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
package edu.uci.ics.texera.service.resource

import edu.uci.ics.amber.config.{EnvironmentalVariable, StorageConfig}
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_DAYS, dayToMin, jwtClaims}
import edu.uci.ics.texera.auth.JwtAuth.{TOKEN_EXPIRE_TIME_IN_MINUTES, jwtClaims}
import edu.uci.ics.texera.auth.{JwtAuth, SessionUser}
import edu.uci.ics.texera.config.{ComputingUnitConfig, KubernetesConfig}
import edu.uci.ics.texera.dao.SqlServer
Expand Down Expand Up @@ -381,7 +381,7 @@ class ComputingUnitManagingResource {
}

val computingUnit = new WorkflowComputingUnit()
val userToken = JwtAuth.jwtToken(jwtClaims(user.user, dayToMin(TOKEN_EXPIRE_TIME_IN_DAYS)))
val userToken = JwtAuth.jwtToken(jwtClaims(user.user, TOKEN_EXPIRE_TIME_IN_MINUTES))
computingUnit.setUid(user.getUid)
computingUnit.setName(param.name)
computingUnit.setCreationTime(new Timestamp(System.currentTimeMillis()))
Expand Down
3 changes: 0 additions & 3 deletions core/config/src/main/resources/auth.conf
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
# Configuration for JWT Authentication. Currently it is used by the FileService to parse the given JWT Token
auth {
jwt {
exp-in-days = 30
exp-in-days = ${?AUTH_JWT_EXP_IN_DAYS}

256-bit-secret = "8a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d"
256-bit-secret = ${?AUTH_JWT_SECRET}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ object AuthConfig {
// Load configuration
private val conf: Config = ConfigFactory.parseResources("auth.conf").resolve()

// Read JWT expiration time
val jwtExpirationDays: Int = conf.getInt("auth.jwt.exp-in-days")

// For storing the generated/configured secret
@volatile private var secretKey: String = _

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
import edu.uci.ics.texera.dao.jooq.generated.tables.User;
import edu.uci.ics.texera.dao.jooq.generated.tables.UserConfig;
import edu.uci.ics.texera.dao.jooq.generated.tables.Workflow;
Expand All @@ -55,6 +56,7 @@
import edu.uci.ics.texera.dao.jooq.generated.tables.records.ProjectRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.ProjectUserAccessRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.PublicProjectRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.TimeLogRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.UserConfigRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.UserRecord;
import edu.uci.ics.texera.dao.jooq.generated.tables.records.WorkflowComputingUnitRecord;
Expand Down Expand Up @@ -98,6 +100,7 @@ public class Keys {
public static final UniqueKey<ProjectRecord> PROJECT_PKEY = Internal.createUniqueKey(Project.PROJECT, DSL.name("project_pkey"), new TableField[] { Project.PROJECT.PID }, true);
public static final UniqueKey<ProjectUserAccessRecord> PROJECT_USER_ACCESS_PKEY = Internal.createUniqueKey(ProjectUserAccess.PROJECT_USER_ACCESS, DSL.name("project_user_access_pkey"), new TableField[] { ProjectUserAccess.PROJECT_USER_ACCESS.UID, ProjectUserAccess.PROJECT_USER_ACCESS.PID }, true);
public static final UniqueKey<PublicProjectRecord> PUBLIC_PROJECT_PKEY = Internal.createUniqueKey(PublicProject.PUBLIC_PROJECT, DSL.name("public_project_pkey"), new TableField[] { PublicProject.PUBLIC_PROJECT.PID }, true);
public static final UniqueKey<TimeLogRecord> TIME_LOG_PKEY = Internal.createUniqueKey(TimeLog.TIME_LOG, DSL.name("time_log_pkey"), new TableField[] { TimeLog.TIME_LOG.UID }, true);
public static final UniqueKey<UserRecord> USER_EMAIL_KEY = Internal.createUniqueKey(User.USER, DSL.name("user_email_key"), new TableField[] { User.USER.EMAIL }, true);
public static final UniqueKey<UserRecord> USER_GOOGLE_ID_KEY = Internal.createUniqueKey(User.USER, DSL.name("user_google_id_key"), new TableField[] { User.USER.GOOGLE_ID }, true);
public static final UniqueKey<UserRecord> USER_PKEY = Internal.createUniqueKey(User.USER, DSL.name("user_pkey"), new TableField[] { User.USER.UID }, true);
Expand Down Expand Up @@ -132,6 +135,7 @@ public class Keys {
public static final ForeignKey<ProjectUserAccessRecord, ProjectRecord> PROJECT_USER_ACCESS__PROJECT_USER_ACCESS_PID_FKEY = Internal.createForeignKey(ProjectUserAccess.PROJECT_USER_ACCESS, DSL.name("project_user_access_pid_fkey"), new TableField[] { ProjectUserAccess.PROJECT_USER_ACCESS.PID }, Keys.PROJECT_PKEY, new TableField[] { Project.PROJECT.PID }, true);
public static final ForeignKey<ProjectUserAccessRecord, UserRecord> PROJECT_USER_ACCESS__PROJECT_USER_ACCESS_UID_FKEY = Internal.createForeignKey(ProjectUserAccess.PROJECT_USER_ACCESS, DSL.name("project_user_access_uid_fkey"), new TableField[] { ProjectUserAccess.PROJECT_USER_ACCESS.UID }, Keys.USER_PKEY, new TableField[] { User.USER.UID }, true);
public static final ForeignKey<PublicProjectRecord, ProjectRecord> PUBLIC_PROJECT__PUBLIC_PROJECT_PID_FKEY = Internal.createForeignKey(PublicProject.PUBLIC_PROJECT, DSL.name("public_project_pid_fkey"), new TableField[] { PublicProject.PUBLIC_PROJECT.PID }, Keys.PROJECT_PKEY, new TableField[] { Project.PROJECT.PID }, true);
public static final ForeignKey<TimeLogRecord, UserRecord> TIME_LOG__TIME_LOG_UID_FKEY = Internal.createForeignKey(TimeLog.TIME_LOG, DSL.name("time_log_uid_fkey"), new TableField[] { TimeLog.TIME_LOG.UID }, Keys.USER_PKEY, new TableField[] { User.USER.UID }, true);
public static final ForeignKey<UserConfigRecord, UserRecord> USER_CONFIG__USER_CONFIG_UID_FKEY = Internal.createForeignKey(UserConfig.USER_CONFIG, DSL.name("user_config_uid_fkey"), new TableField[] { UserConfig.USER_CONFIG.UID }, Keys.USER_PKEY, new TableField[] { User.USER.UID }, true);
public static final ForeignKey<WorkflowComputingUnitRecord, UserRecord> WORKFLOW_COMPUTING_UNIT__WORKFLOW_COMPUTING_UNIT_UID_FKEY = Internal.createForeignKey(WorkflowComputingUnit.WORKFLOW_COMPUTING_UNIT, DSL.name("workflow_computing_unit_uid_fkey"), new TableField[] { WorkflowComputingUnit.WORKFLOW_COMPUTING_UNIT.UID }, Keys.USER_PKEY, new TableField[] { User.USER.UID }, true);
public static final ForeignKey<WorkflowExecutionsRecord, WorkflowComputingUnitRecord> WORKFLOW_EXECUTIONS__WORKFLOW_EXECUTIONS_CUID_FKEY = Internal.createForeignKey(WorkflowExecutions.WORKFLOW_EXECUTIONS, DSL.name("workflow_executions_cuid_fkey"), new TableField[] { WorkflowExecutions.WORKFLOW_EXECUTIONS.CUID }, Keys.WORKFLOW_COMPUTING_UNIT_PKEY, new TableField[] { WorkflowComputingUnit.WORKFLOW_COMPUTING_UNIT.CUID }, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
import edu.uci.ics.texera.dao.jooq.generated.tables.User;
import edu.uci.ics.texera.dao.jooq.generated.tables.UserActivity;
import edu.uci.ics.texera.dao.jooq.generated.tables.UserConfig;
Expand Down Expand Up @@ -108,6 +109,11 @@ public class Tables {
*/
public static final PublicProject PUBLIC_PROJECT = PublicProject.PUBLIC_PROJECT;

/**
* The table <code>texera_db.time_log</code>.
*/
public static final TimeLog TIME_LOG = TimeLog.TIME_LOG;

/**
* The table <code>texera_db.user</code>.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import edu.uci.ics.texera.dao.jooq.generated.tables.Project;
import edu.uci.ics.texera.dao.jooq.generated.tables.ProjectUserAccess;
import edu.uci.ics.texera.dao.jooq.generated.tables.PublicProject;
import edu.uci.ics.texera.dao.jooq.generated.tables.TimeLog;
import edu.uci.ics.texera.dao.jooq.generated.tables.User;
import edu.uci.ics.texera.dao.jooq.generated.tables.UserActivity;
import edu.uci.ics.texera.dao.jooq.generated.tables.UserConfig;
Expand Down Expand Up @@ -122,6 +123,11 @@ public class TexeraDb extends SchemaImpl {
*/
public final PublicProject PUBLIC_PROJECT = PublicProject.PUBLIC_PROJECT;

/**
* The table <code>texera_db.time_log</code>.
*/
public final TimeLog TIME_LOG = TimeLog.TIME_LOG;

/**
* The table <code>texera_db.user</code>.
*/
Expand Down Expand Up @@ -214,6 +220,7 @@ public final List<Table<?>> getTables() {
Project.PROJECT,
ProjectUserAccess.PROJECT_USER_ACCESS,
PublicProject.PUBLIC_PROJECT,
TimeLog.TIME_LOG,
User.USER,
UserActivity.USER_ACTIVITY,
UserConfig.USER_CONFIG,
Expand Down
Loading
Loading