Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package clear.dao
- import clear.api.intf.{AuthImportFacility, ReportType}
- import clear.api.intf.frontend.ChangePasswordResult
- import clear.api.intf.frontend.ProviderFrontend
- import clear.db._
- import UserType._
- import scalaz._
- import Scalaz._
- import slick.driver.MySQLDriver.simple._
- import org.apache.commons.codec.binary.Base64._
- import org.apache.commons.codec.binary.Hex._
- import org.mindrot.jbcrypt.BCrypt
- import java.sql.Timestamp
- import java.time.Instant
- case class UserDao(db:Adapter) extends AbstractUserDao {
- // Tables
- val user = TableQuery[TblUser]
- val userFeature = TableQuery[TblUserFeature]
- val userAccess = TableQuery[VwUserAccess]
- val workItem = TableQuery[TblWorkItem]
- val facility = TableQuery[TblFacility]
- val pubKey = TableQuery[TblUserPubKey]
- val reports = TableQuery[TblWorkItemReport]
- val changePassword = TableQuery[TblChangePassword]
- val provider = TableQuery[TblProvider]
- val rng = new java.security.SecureRandom
- val dxIcd10New = TableQuery[NewVwIcd10CmNew]
- val dxIcd10PcsNew = TableQuery[VwIcd10PcsNew]
- val CHANGE_PASSWORD_TOKEN_BYTES = 8
- /**
- * Generate a bunch of random hex digits.
- */
- private def randHexToken(numBytes:Int):String = {
- val rnd = Array.fill(numBytes)(0:Byte)
- rng.nextBytes(rnd)
- encodeHexString(rnd)
- }
- override def getEffectiveDates: List[String] =
- db.connect.withSession { implicit session =>
- val icd10 = dxIcd10New.map(_.effective_date).list //.groupBy(_.effective_date).list.map(_._1)
- val icd10Pcs = dxIcd10PcsNew.map(_.effective_date).list
- (icd10 ++ icd10Pcs).sorted.distinct
- }
- override def updatePass(id: Int, newHash: String): Unit =
- db.connect.withSession { implicit session =>
- user.filter(_.user_uid === id)
- .map(_.user_password)
- .update(newHash)
- }
- override def findUser(email: String): Option[User] =
- db.connect.withSession { implicit session =>
- user.filter(u => u.user_email_address === email && u.user_active)
- .firstOption
- }
- override def findUserProvider(id: Int): Option[ProviderFrontend] =
- db.connect.withSession { implicit session =>
- provider.filter(_.user_uid === id)
- .map(p => (p.provider_uid,p.provider_lov,p.provider_email,p.display_name, p.type_id))
- .firstOption
- .map((ProviderFrontend.apply _).tupled)
- }
- override def featuresForUser(userId: Int): List[String] =
- db.connect.withSession { implicit session =>
- val liveCode =
- (for {
- me <- userAccess if me.user_uid === userId
- scribe <- userAccess if scribe.work_item_uid === me.work_item_uid &&
- scribe.user_type === ENCOUNTER_CREATE
- } yield scribe).exists.run
- userFeature.filter(_.user_uid === userId)
- .map(_.feature_id)
- .list ++
- (liveCode ?? List("LIVECODE")) // Include this if any of this user's work items have a LiveCode scribe.
- }
- override def workItemRoles(userId: Int): Map[String,List[AuthImportFacility]] =
- db.connect.withSession { implicit session =>
- (for {
- ua <- userAccess if ua.user_uid === userId
- wi <- workItem if wi.work_item_uid === ua.work_item_uid
- fac <- facility if fac.facility_id === wi.facility_id
- } yield (ua.user_type, fac.client_disposition_name, wi.work_item_uid, fac.timezone)
- ).list
- .groupBy(_._1)
- .map{ case (userType,workItems) => // note that we can't just use mapValues here, as it is non-strict (DB session escapes this scope)
- (userType -> workItems.map {
- case (`REPORT`, facId, workItemId, tz) =>
- AuthImportFacility(facId, workItemId, tz,
- reports.filter(_.work_item_uid === workItemId)
- .map(r => (r.report_id,r.extension))
- .list
- .collect{ // Any key that can't map to a ReportType will be skipped
- case (ReportType(reportType),ext) => (reportType,ext)
- } )
- case (_, facId, workItemId, tz) =>
- AuthImportFacility(facId, workItemId, tz, Nil)
- } )
- }
- }
- // FIXME: This will throw on bad Base64
- override def getRsaPublicKeys(username: String): List[Array[Byte]] =
- db.connect.withSession { implicit session =>
- (for {
- u <- user if u.user_email_address === username
- k <- pubKey if k.user_uid === u.user_uid
- } yield k.public_key
- ).list.map(decodeBase64(_))
- }
- override def authorizePasswordChange(email: String): Option[String] =
- db.connect.withTransaction { implicit session =>
- val userForEmail = user.filter(_.user_email_address === email).firstOption
- userForEmail.map{
- case userInfo => {
- // TODO: Possibly do some authorization checks. In the most common case,
- // a user won't be logged in, exactly because he doesn't remember his password.
- // But we might want to deny this feature to users who are already logged in,
- // or against certain types of users (admins, etc.).
- // Remove any existing change-password-authorizations for this user
- changePassword.filter(existingUser => existingUser.user_uid === userInfo.id).delete
- // Add a new row to the changePassword table, authorizing anyone with the
- // newly created token to set the password for the user. We're using
- // hex for the token instead of base64 or whatever, so that we don't have
- // to worry about conflict with URL characters.
- val expTimeSec = 12 * 60 * 60 // 12 hours
- val requestToken = randHexToken(CHANGE_PASSWORD_TOKEN_BYTES)
- val expDate = Timestamp.from(Instant.now.plusSeconds(expTimeSec))
- changePassword += (0, userInfo.id, requestToken, expDate)
- // Return the request token
- requestToken
- }
- }
- }
- // TODO: Probably can do better than strings for the return status
- override def changePasswordByEmail(email: String, authorizationCode: String, newPassword: String): ChangePasswordResult =
- db.connect.withTransaction { implicit session =>
- val userIdOpt = user.filter(_.user_email_address === email).firstOption.map{u: User => u.id}
- val authRecOpt:Option[(Int,Int,String,Timestamp)] = userIdOpt.map{
- case userId => changePassword.filter(r => r.user_uid===userId && r.request_token===authorizationCode)
- .firstOption
- }.flatten
- authRecOpt match {
- case Some((id, user_uid, request_token, expires_utc)) => {
- if ( expires_utc.toInstant.compareTo(Instant.now)<=0 ) {
- ChangePasswordResult.AuthExpired
- } else {
- // Hash the new password and stuff it in the DB.
- val pwHash = BCrypt.hashpw(newPassword, BCrypt.gensalt)
- user.filter(_.user_uid === user_uid).map(u => (u.user_password)).update(pwHash)
- // Delete the old password change authorization record.
- changePassword.filter(_.user_uid===user_uid).delete
- // TODO: Add some sort of audit trail record
- ChangePasswordResult.Success
- }
- }
- case None => ChangePasswordResult.BadUserOrToken
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement