diff --git a/backend/src/main/scala/industries/sunshine/planningpoker/Auth.scala b/backend/src/main/scala/industries/sunshine/planningpoker/Auth.scala index 7aae41d..d250e8e 100644 --- a/backend/src/main/scala/industries/sunshine/planningpoker/Auth.scala +++ b/backend/src/main/scala/industries/sunshine/planningpoker/Auth.scala @@ -1,7 +1,7 @@ package industries.sunshine.planningpoker import cats.effect._ -import cats.syntax._ +import cats.syntax.all._ import cats.data.Kleisli import cats.data.OptionT import org.http4s.Request @@ -9,6 +9,7 @@ import industries.sunshine.planningpoker.common.Models.PlayerID import java.util.UUID import industries.sunshine.planningpoker.common.Models.{RoomID, Room} import org.http4s.ResponseCookie +import cats.data.EitherT trait Auth[F[_]] { @@ -26,7 +27,8 @@ trait Auth[F[_]] { def joinRoom( roomName: String, roomPassword: String, - nickName: String + nickName: String, + nickPassword: String ): F[Either[Unit, ResponseCookie]] def deleteSession( @@ -48,7 +50,8 @@ object Auth { override def joinRoom( roomName: String, roomPassword: String, - nickName: String + nickName: String, + nickPassword: String ) = IO( println( @@ -61,6 +64,7 @@ object Auth { } type SessionsMap = Map[Long, (RoomID, PlayerID)] + class SimpleAuth[F[_]: Sync]( sessions: Ref[F, SessionsMap], roomService: RoomService[F] @@ -69,8 +73,12 @@ object Auth { override def joinRoom( roomName: String, roomPassword: String, - nickName: String + nickName: String, + nickPassword: String ): 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]] = for { sessionsMap <- Ref.of[F, SessionsMap]( diff --git a/backend/src/main/scala/industries/sunshine/planningpoker/MyHttpService.scala b/backend/src/main/scala/industries/sunshine/planningpoker/MyHttpService.scala index 6322431..53c6691 100644 --- a/backend/src/main/scala/industries/sunshine/planningpoker/MyHttpService.scala +++ b/backend/src/main/scala/industries/sunshine/planningpoker/MyHttpService.scala @@ -59,8 +59,8 @@ object MyHttpService { authResult <- auth.joinRoom( data.roomName, data.password, - data.nickname - ) + data.nickname, + data.nickPassword) resp <- authResult match { case Left(error) => Forbidden(error) diff --git a/backend/src/main/scala/industries/sunshine/planningpoker/RoomService.scala b/backend/src/main/scala/industries/sunshine/planningpoker/RoomService.scala index 88e425f..1da27c9 100644 --- a/backend/src/main/scala/industries/sunshine/planningpoker/RoomService.scala +++ b/backend/src/main/scala/industries/sunshine/planningpoker/RoomService.scala @@ -3,14 +3,24 @@ package industries.sunshine.planningpoker import industries.sunshine.planningpoker.common.Models.* import cats.effect.{Ref, Sync} import cats.syntax.all._ +import cats.data.EitherT enum RoomError { case RoomAlreadyExists(name: String) + case RoomMissing(name: String) + case RoomPassIncorrect + case NickPassIncorrect } trait RoomService[F[_]] { def createRoom(newRoom: Room): F[Either[RoomError, Room]] 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 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)) } - 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 { diff --git a/common/src/main/scala/industries/sunshine/planningpoker/Models.scala b/common/src/main/scala/industries/sunshine/planningpoker/Models.scala index d179786..fc87f7a 100644 --- a/common/src/main/scala/industries/sunshine/planningpoker/Models.scala +++ b/common/src/main/scala/industries/sunshine/planningpoker/Models.scala @@ -62,7 +62,8 @@ object Models { owner: PlayerID, password: String, allowedCards: List[String], - round: RoundState + round: RoundState, + playersPasswords: Map[String, String] = Map.empty // nickname into password ) { def toViewFor(playerId: PlayerID): RoomStateView = { players diff --git a/common/src/main/scala/industries/sunshine/planningpoker/Requests.scala b/common/src/main/scala/industries/sunshine/planningpoker/Requests.scala index 5349a9c..d1fcf0b 100644 --- a/common/src/main/scala/industries/sunshine/planningpoker/Requests.scala +++ b/common/src/main/scala/industries/sunshine/planningpoker/Requests.scala @@ -4,6 +4,6 @@ import io.circe.generic.semiauto._ import io.circe._ 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 }