diff --git a/Package.swift b/Package.swift index 4dc0ce0..6c04871 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ let package = Package( name: "PerfectTurnstileMySQL", targets: [], dependencies: [ - .Package(url: "https://github.com/SwiftORM/MySQL-StORM.git", majorVersion: 1, minor: 0), + .Package(url: "https://github.com/nawar/MySQL-StORM.git", majorVersion: 1, minor: 0), .Package(url: "https://github.com/PerfectlySoft/Perfect-HTTPServer.git", majorVersion: 2, minor: 1), .Package(url: "https://github.com/PerfectlySoft/Perfect-Mustache.git", majorVersion: 2, minor: 0), .Package(url: "https://github.com/iamjono/SwiftString.git", majorVersion: 1, minor: 0), diff --git a/Sources/Account.swift b/Sources/Account.swift index b3120d1..4146d63 100755 --- a/Sources/Account.swift +++ b/Sources/Account.swift @@ -28,6 +28,9 @@ open class AuthAccount : MySQLStORM, Account { /// Stored Google ID when logging in with Google public var googleID: String = "" + + /// Stored LinkedIn ID when logging in with Google + public var linkedInID: String = "" /// Optional first name public var firstname: String = "" @@ -40,10 +43,16 @@ open class AuthAccount : MySQLStORM, Account { /// Internal container variable for the current Token object public var internal_token: AccessTokenStore = AccessTokenStore() - - /// The table to store the data + + /// xmpp username + public var _xmpp_un: String = "" + + /// xmpp password + public var _xmpp_pw: String = "" + + /// The table to store the data override open func table() -> String { - return "users" + return "people" } /// Shortcut to store the id @@ -58,9 +67,12 @@ open class AuthAccount : MySQLStORM, Account { password = this.data["password"] as? String ?? "" facebookID = this.data["facebookID"] as? String ?? "" googleID = this.data["googleID"] as? String ?? "" + linkedInID = this.data["linkedInID"] as? String ?? "" firstname = this.data["firstname"] as? String ?? "" lastname = this.data["lastname"] as? String ?? "" email = this.data["email"] as? String ?? "" + _xmpp_un = this.data["xmpp_un"] as? String ?? "" + _xmpp_pw = this.data["xmpp_pw"] as? String ?? "" } /// Iterate through rows and set to object data @@ -88,7 +100,14 @@ open class AuthAccount : MySQLStORM, Account { func get(_ un: String, _ pw: String) throws -> AuthAccount { let cursor = StORMCursor(limit: 1, offset: 0) do { - try select(whereclause: "username = ?", params: [un], orderby: [], cursor: cursor) + let joins = StORMDataSourceJoin(table: "users", + onCondition: "people.uniqueID = users.people_uniqueID", + direction: .INNER) + try select(columns: ["people.username", "people.password", + "users.username as xmpp_un","users.password as xmpp_pw"], + whereclause: "people.username = ?", params: [un], orderby: [], cursor: cursor, + joins: [joins], having: [], groupBy: []) + if self.results.rows.count == 0 { throw StORMError.noRecordFound } @@ -120,5 +139,3 @@ open class AuthAccount : MySQLStORM, Account { } } } - - diff --git a/Sources/AuthHandlersJSON.swift b/Sources/AuthHandlersJSON.swift index b6acfa3..0f93309 100755 --- a/Sources/AuthHandlersJSON.swift +++ b/Sources/AuthHandlersJSON.swift @@ -6,11 +6,12 @@ // // - import PerfectLib import PerfectHTTP import PerfectMustache import StORM +import MySQL +import MySQLStORM import Foundation import TurnstilePerfect @@ -18,6 +19,12 @@ import Turnstile import TurnstileCrypto import TurnstileWeb +#if os(Linux) + import SwiftGlibc +#else + import Darwin +#endif + /// public var that houses the Token object public var tokenStore: AccessTokenStore? @@ -55,6 +62,12 @@ public class AuthHandlersJSON { resp["error"] = "none" resp["login"] = "ok" resp["token"] = token + + if let authAccount = request.user.authDetails?.account as? AuthAccount { + resp["xmpp_un"] = authAccount._xmpp_un + resp["xmpp_pw"] = authAccount._xmpp_pw + } + } catch { resp["error"] = "Invalid username or password" } @@ -94,17 +107,62 @@ public class AuthHandlersJSON { do { try request.user.register(credentials: credentials) - + + // if the registeration is good, register for the xmpp as well + if let authAccount = request.user.authDetails?.account as? AuthAccount { + + // now register for the xmpp server using ejablibswiftCore.soberdctl + let res = try runProc(cmd: "ejabberdctl", args: ["register",username, "localhost", password]) + Log.info(message: "Ejabberdctl result:\(res)") + + // using the returned data, update the just created ejabberd user with + // foreign key of people_uniqueID + + let server = MySQL() + let conn = server.connect(host: MySQLConnector.host , user: MySQLConnector.username, password: MySQLConnector.password, db: MySQLConnector.database, port: UInt32(MySQLConnector.port)) + + if conn { + + let sql = "UPDATE users SET people_uniqueID = '\(authAccount.uniqueID)' WHERE username = '\(authAccount.username)'" + if server.query(statement: sql) { + Log.info(message: "Successfull update") + } else { + Log.warning(message: "Update issue") + } + + defer { server.close() } + + } else { + Log.warning(message: "Issue with opening:\(server.errorMessage())") + } + } + + // login as you go and send the user the info try request.user.login(credentials: credentials) + //register resp["error"] = "none" resp["login"] = "ok" resp["token"] = response.request.user.authDetails?.sessionID + + if let authAccount = request.user.authDetails?.account as? AuthAccount { + resp["xmpp_un"] = authAccount._xmpp_un + resp["xmpp_pw"] = authAccount._xmpp_pw + } + } catch let e as TurnstileError { resp["error"] = e.description - } catch { - resp["error"] = "An unknown error occurred." - } + } catch let e as PerfectError { + if case .systemError(let (_,text)) = e { + Log.debug(message: text) + resp["error"] = text + } + resp["error"] = "An unknown error occurred. \(e.localizedDescription)" + } catch let e { + Log.debug(message: e.localizedDescription) + resp["error"] = e.localizedDescription + } + do { try response.setBody(json: resp) } catch { @@ -127,6 +185,13 @@ public class AuthHandlersJSON { response.setHeader(.contentType, value: "application/json") var resp = [String: String]() + // Destroy the token and clean the token store + if let headerValue = request.header(HTTPRequestHeader.Name.authorization) { + if let res = tokenStore?.destroy(headerValue), !res { + Log.warning(message: "Couldn't delete token from the store") + } + } + request.user.logout() resp["error"] = "none" resp["logout"] = "complete" @@ -258,5 +323,33 @@ public class AuthHandlersJSON { } response.completed() } + + private static func runProc(cmd: String, args: [String], read: Bool = false) throws -> String? { + let envs = [("PATH", "/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin")] + let proc = try SysProcess(cmd, args: args, env: envs) + var ret: String? + if read { + var ary = [UInt8]() + while true { + do { + guard let s = try proc.stdout?.readSomeBytes(count: 1024), s.count > 0 else { + break + } + ary.append(contentsOf: s) + } catch PerfectLib.PerfectError.fileError(let code, _) { + if code != EINTR { + break + } + } + } + ret = UTF8Encoding.encode(bytes: ary) + } + let res = try proc.wait(hang: true) + if res != 0 { + let s = try proc.stderr?.readString() + throw PerfectError.systemError(Int32(res), s!) + } + return ret + } } diff --git a/Sources/Tokens.swift b/Sources/Tokens.swift index 6e60e06..cdf704f 100755 --- a/Sources/Tokens.swift +++ b/Sources/Tokens.swift @@ -91,4 +91,18 @@ open class AccessTokenStore : MySQLStORM { } return token } + + /// Destroy the token, after logout, by deleting it from token store + public func destroy(_ headerValue: String) -> Bool { + + guard let range = headerValue.range(of: "Bearer ") else { return false } + + let token = headerValue.substring(from: range.upperBound) + do { + try delete(token, idName: "token") + } catch { + print(error) + } + return true + } }