1
votes

I'm attempting to write a function that takes in my UserModel, and preforms a couple of checks to see if the user

  1. Is locked
  2. Hasn't attempted too many logins within a time period.

and then returns a Boolean indication of the result.

The lookups work within my Authentication process. But I wanted to break out the code that determines if the user is allowed to (attempt) login so I can use it in more than one place without repeating code.

However (being new to Vapor/Swift) i am getting an error and I cannot work out what I'm doing wrong: Cannot convert return expression of type 'EventLoopFuture' to return type 'Bool'

The Error is on the line }.all().map { (separated onto a line by itself so its easier to find).

Database structure wise I have 2 tables involved:

  • UserAccess which holds my user profile details (how many bad attempts this user can have, and how far back in the log do we look for login attempts)
  • UserLog, which hows the login attempts for each user and when they made the attempt

This is my code snippet so far:

func CanUserLogin(user: UserModel, req: Request) -> EventLoopFuture<Bool> {
  if(!(user.locked ?? false)) {
    let userProfileId = user.userprofile

    return Usertype.query(on: req.db)
      .filter(\.$profilenum == userProfileId)
      .first().map { useraccess in
        let badloginperiod = Double((useraccess!.badloginperiod ?? 0) * -1 * 60) // convert minutes to seconds (we need a negative number)
        let lookUpDate = Date().addingTimeInterval(badloginperiod)

        return Userlog.query(on: req.db).group(.and) {
          and in
          and.filter(\.$username == user.username)
          and.filter(\.$datecreated >= lookUpDate)
        }.all().map {
          UserLogs -> Bool in
          let value = userLogs.count

          // the account is locked or the max attempts for the time peroid
          if(value >= (useraccess.maxloginattempts ?? 3)) {
            return false
          } else {
            return true
          }
        }
    }
  }
}

Any direction would be greatly appreciated.

1
You could read this great article about futures, it should clarify a lot of things 🙂 – imike

1 Answers

2
votes

You tried to return EventLoopFuture from map block, but you can return only non-future values from it. So instead of map you have to use flatMap on Usertype query.

Checkout this code

func canUserLogin(user: UserModel, req: Request) -> EventLoopFuture<Bool> {
    guard user.locked != true else {
        return req.eventLoop.makeFailedFuture(Abort(.badRequest, reason: "User is locked"))
    }
    let userProfileId = user.userprofile
    return Usertype.query(on: req.db)
        .filter(\.$profilenum == userProfileId)
        .first()
        .unwrap(or: Abort(.forbidden, reason: "No access"))
        .flatMap { userAccess in
            let badloginperiod = Double((useraccess.badloginperiod ?? 0) * -1 * 60) // convert minutes to seconds (we need a negative number)
            let lookUpDate = Date().addingTimeInterval(badloginperiod)
            return Userlog.query(on: req.db).group(.and) {
              $0.filter(\.$username == user.username)
              $0.filter(\.$datecreated >= lookUpDate)
            }.all().map { attempts -> Bool in
                // the account is locked or the max attempts for the time peroid
                if attempts.count >= (userAccess.maxloginattempts ?? 3) {
                    return false
                } else {
                    return true
                }
            }
    }
}