Swift and Vapor;は初めてです。
UserModel(Fluent Modelオブジェクト)から呼び出され、必要なアクセスレベル(int)とパス(string)が過ぎている承認ミドルウェアを開発しようとしています。
私は数日間試してみましたが、いつも近づいてきましたが、自分が求めているものをまったく得ていません。
私は次のものを持っています(名前の悪いオブジェクトを許してください):
import Vapor
public protocol Authorizable: Authenticatable {
associatedtype ProfileAccess: UserAuthorizable
}
/// status cached using `UserAuthorizable'
public protocol UserAuthorizable: Authorizable {
/// Session identifier type.
associatedtype AccessLevel: LosslessStringConvertible
/// Identifier identifier.
var accessLevel: AccessLevel { get }
}
extension Authorizable {
/// Basic middleware to redirect unauthenticated requests to the supplied path
///
/// - parameters:
/// - path: The path to redirect to if the request is not authenticated
public static func authorizeMiddleware(levelrequired: Int, path: String) -> Middleware {
return AuthorizeUserMiddleware<Self>(Self.self, levelrequired: levelrequired, path: path)
}
}
// Helper for creating authorization middleware.
///
//private final class AuthRedirectMiddleware<A>: Middleware
//where A: Authenticatable
final class AuthorizeUserMiddleware<A>: Middleware where A: Authorizable {
let levelrequired: Int
let path: String
let authLevel : Int
init(_ authorizableType: A.Type = A.self, levelrequired: Int, path: String) {
self.levelrequired = levelrequired
self.path = path
}
/// See Middleware.respond
public func respond(to req: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
if req.auth.has(A.self) {
print("--------")
print(path)
print(levelrequired)
**// print(A.ProfileAccess.AccessLevel) <- list line fails because the A.ProfileAccess.AccessLevel is a type not value**
print("--------")
}
return next.respond(to: req)
}
}
ユーザーモデルに以下を追加しました
extension UserModel: Authorizable
{
typealias ProfileAccess = UserModel
}
extension UserModel: UserAuthorizable {
typealias AccessLevel = Int
var accessLevel: AccessLevel { self.userprofile! }
}
そのようなルート
// setup the authentication process
let session = app.routes.grouped([
UserModelSessionAuthenticator(),
UserModelCredentialsAuthenticator(),
UserModel.authorizeMiddleware(levelrequired: 255, path: "/login"), // this will redirect the user when unauthenticted
UserModel.authRedirectMiddleware(path: "/login"), // this will redirect the user when unauthenticted
])
パスと必要なレベルは正しく渡されますが、現在のユーザーからAccessLevelのインスタンスを取得できません。 (私は自分のc ++帽子をかぶっていて、認証からすでに推測されているUserModelは実際にはユーザーの入力済みインスタンスではありません)
関連するタイプを使用してアカウント情報を渡す「SessionAuthenticator」プロセスにマージしようとしました。
私のもう1つの考えは、ユーザーが認証されているかどうかを確認し、そうであればセッションCookieにユーザーIDが安全に含まれていると想定できるため、DBからユーザーを(再度)プルして、そこからユーザーのアクセスレベルを確認することでした。
私はここから道を外れるかもしれませんが、数日後にどちらの道が最善のアプローチかわからないので、どんなガイダンスもいただければ幸いです。
乾杯
私はUser
モデルをModelAuthenticatable
に準拠させるVapor 4アプローチを使用します:
extension User:ModelAuthenticatable
{
static let usernameKey = \User.$email
static let passwordHashKey = \User.$password
func verify(password: String) throws -> Bool
{
try Bcrypt.verify(password, created:self.password)
}
}
ユーザーが存在し、上記のようにパスワードが検証されたことを確認した時点で、ユーザーをログインさせます。
request.auth.login(user)
ログインしたら、ユーザーがログインしているかどうかを確認するカスタムMiddleware
と「スーパーユーザー」を使用します。ログインしている場合は、チェーン内の次のMiddleware
に応答が渡されます。それ以外の場合は、ホーム/ログインページにリダイレクトします。
struct SuperUserMiddleware:Middleware
{
func respond(to request:Request, chainingTo next:Responder) -> EventLoopFuture<Response>
{
do
{
let user = try request.auth.require(User.self)
if user.superUser { return next.respond(to:request) }
}
catch {}
let redirect = request.redirect(to:"UNPRIVILEGED")
return request.eventLoop.makeSucceededFuture(redirect)
}
}
私はSuperUserMiddleware
を登録し、configure.Swift
の特定のルートグループで使用します。
app.middleware.use(SessionsMiddleware(session:MemorySessions(storage:MemorySessions.Storage())))
app.middleware.use(User.sessionAuthenticator(.mysql))
let superUserMW = SuperUserMiddleware()
let userAuthSessionsMW = User.authenticator()
let events = app.grouped("/events").grouped(userAuthSessionsMW, superUserMW)
try EventRoutes(events)