adding room service, joining room

adding nick password into model, stored in the Room
to allow more secure joining for repeated times
This commit is contained in:
efim 2023-04-25 11:41:32 +04:00
parent 2244f38348
commit 1b71c942ec
5 changed files with 53 additions and 12 deletions

View File

@ -1,7 +1,7 @@
package industries.sunshine.planningpoker package industries.sunshine.planningpoker
import cats.effect._ import cats.effect._
import cats.syntax._ import cats.syntax.all._
import cats.data.Kleisli import cats.data.Kleisli
import cats.data.OptionT import cats.data.OptionT
import org.http4s.Request import org.http4s.Request
@ -9,6 +9,7 @@ import industries.sunshine.planningpoker.common.Models.PlayerID
import java.util.UUID import java.util.UUID
import industries.sunshine.planningpoker.common.Models.{RoomID, Room} import industries.sunshine.planningpoker.common.Models.{RoomID, Room}
import org.http4s.ResponseCookie import org.http4s.ResponseCookie
import cats.data.EitherT
trait Auth[F[_]] { trait Auth[F[_]] {
@ -26,7 +27,8 @@ trait Auth[F[_]] {
def joinRoom( def joinRoom(
roomName: String, roomName: String,
roomPassword: String, roomPassword: String,
nickName: String nickName: String,
nickPassword: String
): F[Either[Unit, ResponseCookie]] ): F[Either[Unit, ResponseCookie]]
def deleteSession( def deleteSession(
@ -48,7 +50,8 @@ object Auth {
override def joinRoom( override def joinRoom(
roomName: String, roomName: String,
roomPassword: String, roomPassword: String,
nickName: String nickName: String,
nickPassword: String
) = ) =
IO( IO(
println( println(
@ -61,6 +64,7 @@ object Auth {
} }
type SessionsMap = Map[Long, (RoomID, PlayerID)] type SessionsMap = Map[Long, (RoomID, PlayerID)]
class SimpleAuth[F[_]: Sync]( class SimpleAuth[F[_]: Sync](
sessions: Ref[F, SessionsMap], sessions: Ref[F, SessionsMap],
roomService: RoomService[F] roomService: RoomService[F]
@ -69,8 +73,12 @@ object Auth {
override def joinRoom( override def joinRoom(
roomName: String, roomName: String,
roomPassword: String, roomPassword: String,
nickName: String nickName: String,
nickPassword: String
): F[Either[Unit, ResponseCookie]] = { ): F[Either[Unit, ResponseCookie]] = {
for {
joinedRoom <- roomService.joinRoom(RoomID(roomName), nickName, nickPassword, roomPassword)
} yield ()
??? ???
} }
@ -86,7 +94,6 @@ object Auth {
} }
} }
import cats.Monad.ops.toAllMonadOps
def make[F[_]: Sync](roomsService: RoomService[F]): F[Auth[F]] = def make[F[_]: Sync](roomsService: RoomService[F]): F[Auth[F]] =
for { for {
sessionsMap <- Ref.of[F, SessionsMap]( sessionsMap <- Ref.of[F, SessionsMap](

View File

@ -59,8 +59,8 @@ object MyHttpService {
authResult <- auth.joinRoom( authResult <- auth.joinRoom(
data.roomName, data.roomName,
data.password, data.password,
data.nickname data.nickname,
) data.nickPassword)
resp <- authResult match { resp <- authResult match {
case Left(error) => case Left(error) =>
Forbidden(error) Forbidden(error)

View File

@ -3,14 +3,24 @@ package industries.sunshine.planningpoker
import industries.sunshine.planningpoker.common.Models.* import industries.sunshine.planningpoker.common.Models.*
import cats.effect.{Ref, Sync} import cats.effect.{Ref, Sync}
import cats.syntax.all._ import cats.syntax.all._
import cats.data.EitherT
enum RoomError { enum RoomError {
case RoomAlreadyExists(name: String) case RoomAlreadyExists(name: String)
case RoomMissing(name: String)
case RoomPassIncorrect
case NickPassIncorrect
} }
trait RoomService[F[_]] { trait RoomService[F[_]] {
def createRoom(newRoom: Room): F[Either[RoomError, Room]] def createRoom(newRoom: Room): F[Either[RoomError, Room]]
def updateRoom(room: Room): F[Unit] def updateRoom(room: Room): F[Unit]
def joinRoom(
id: RoomID,
nickName: String,
nickPassword: String,
roomPassword: String
): F[Either[RoomError, Room]]
def deleteRoom(roomID: RoomID): F[Unit] def deleteRoom(roomID: RoomID): F[Unit]
def getRoom(roomID: RoomID): F[Option[Room]] def getRoom(roomID: RoomID): F[Option[Room]]
} }
@ -26,13 +36,36 @@ class InMemoryRoomService[F[_]: Sync](stateRef: Ref[F, Map[RoomID, Room]]) exten
} }
} }
} }
def updateRoom(room: Room): F[Unit] = stateRef.update { state => override def updateRoom(room: Room): F[Unit] = stateRef.update { state =>
state.get(room.id).fold(state)(oldRoom => state.updated(room.id, room)) state.get(room.id).fold(state)(oldRoom => state.updated(room.id, room))
} }
def deleteRoom(roomID: RoomID): F[Unit] = stateRef.update(_.removed(roomID)) override def deleteRoom(roomID: RoomID): F[Unit] = stateRef.update(_.removed(roomID))
def getRoom(roomID: RoomID): F[Option[Room]] = stateRef.get.map(_.get(roomID)) override def getRoom(roomID: RoomID): F[Option[Room]] = stateRef.get.map(_.get(roomID))
override def joinRoom(
id: RoomID,
nickName: String,
nickPassword: String,
roomPassword: String
): F[Either[RoomError, Room]] = stateRef.modify[Either[RoomError, Room]] { rooms =>
val joinedRoom = for {
room <- rooms.get(id).toRight(RoomError.RoomMissing(id.name))
_ <- Either.cond(room.password == roomPassword, (), RoomError.RoomPassIncorrect)
isNickPassCorrect = room.playersPasswords
.get(nickName)
.fold(true)(existingPass => existingPass == nickPassword)
_ <- Either.cond(
isNickPassCorrect,
(),
RoomError.NickPassIncorrect
)
updatedRoom = room
} yield updatedRoom
rooms -> joinedRoom
}
} }
object RoomService { object RoomService {

View File

@ -62,7 +62,8 @@ object Models {
owner: PlayerID, owner: PlayerID,
password: String, password: String,
allowedCards: List[String], allowedCards: List[String],
round: RoundState round: RoundState,
playersPasswords: Map[String, String] = Map.empty // nickname into password
) { ) {
def toViewFor(playerId: PlayerID): RoomStateView = { def toViewFor(playerId: PlayerID): RoomStateView = {
players players

View File

@ -4,6 +4,6 @@ import io.circe.generic.semiauto._
import io.circe._ import io.circe._
object Requests { object Requests {
final case class LogIn(roomName: String, nickname: String, password: String) final case class LogIn(roomName: String, nickname: String, password: String, nickPassword: String)
derives Codec.AsObject derives Codec.AsObject
} }