From bc07b68c1301e77707788482c9677675366aec23 Mon Sep 17 00:00:00 2001 From: Elyes Date: Fri, 11 Aug 2023 10:55:36 +0100 Subject: [PATCH] add authentications --- .../Extensions/Security/Authentication.swift | 53 ++++++++- .../Security/AuthenticationType.swift | 50 +++++++++ .../Extensions/Security/Bearer.swift | 14 ++- .../Extensions/Security/Custom.swift | 65 +++++++++++ .../Extensions/Security/Simple.swift | 28 ++++- .../Utility/ByteBuffer+UInt24.swift | 11 ++ .../Extensions/Security/SecurityTests.swift | 102 ++++++++++++++++++ 7 files changed, 316 insertions(+), 7 deletions(-) create mode 100644 Sources/RSocketCore/Extensions/Security/Custom.swift create mode 100644 Tests/RSocketCoreTests/Extensions/Security/SecurityTests.swift diff --git a/Sources/RSocketCore/Extensions/Security/Authentication.swift b/Sources/RSocketCore/Extensions/Security/Authentication.swift index 74d843b0..68f8e705 100644 --- a/Sources/RSocketCore/Extensions/Security/Authentication.swift +++ b/Sources/RSocketCore/Extensions/Security/Authentication.swift @@ -19,20 +19,46 @@ import NIOCore public struct Authentication { public var type: AuthenticationType public var payload: ByteBuffer + public init(type: AuthenticationType, payload: ByteBuffer) { + self.type = type + self.payload = payload + } } public struct AuthenticationDecoder: MetadataDecoder { @inlinable public var mimeType: MIMEType { .messageXRSocketAuthenticationV0 } - @inlinable public func decode(from buffer: inout ByteBuffer) throws -> Authentication { - fatalError("not implemented") + let idOrLength = buffer.readBytes(length: 1)?.withUnsafeBytes{ value -> UInt8 in + return value.load(as: UInt8.self) + } + debugPrint(idOrLength! & 0x7F) + debugPrint(idOrLength!) + switch idOrLength! & 0x7F { + case WellKnownAuthenticationType.SIMPLE.identifier : + return Authentication(type: AuthenticationType.simple,payload: buffer) + case WellKnownAuthenticationType.BEARER.identifier: + return Authentication(type: AuthenticationType.bearer ,payload: buffer) + default : + return Authentication(type: AuthenticationType(rawValue: buffer.readString(length: Int(idOrLength!)) ?? "") ,payload: buffer) + } + } } extension MetadataDecoder where Self == AuthenticationDecoder { public static var authentication: Self { .init() } + + //Return ID or Auth Length + public static func isWellKnownAuthType(_ buffer : ByteBuffer) -> Bool{ + var tmp = buffer + guard let authType = (tmp.readBytes(length: 1)?.withUnsafeBytes{ value -> UInt8 in return value.load(as: UInt8.self).bigEndian}) else{ + return false + } + let idOrLength = UInt8(authType & 0x7F) + return idOrLength != authType + } } public struct AuthenticationEncoder: MetadataEncoder { @@ -41,7 +67,28 @@ public struct AuthenticationEncoder: MetadataEncoder { @inlinable public func encode(_ metadata: Authentication, into buffer: inout ByteBuffer) throws { - fatalError("not implemented") + switch metadata.type{ + case .bearer: + buffer.mergeByteBuffers(buffers: [ByteBuffer(integer: UInt8(WellKnownAuthenticationType.BEARER.identifier )), metadata.payload]) + case .simple: + buffer.mergeByteBuffers(buffers: [ByteBuffer(integer: UInt8(WellKnownAuthenticationType.SIMPLE.identifier)), + metadata.payload]) + default: + try encodeCustomMetadata(customAuthType: metadata.type.rawValue, metadata: metadata.payload , into: &buffer) + } + } + + @inlinable + public func encodeCustomMetadata(customAuthType: String, metadata: ByteBuffer , into buffer: inout ByteBuffer) throws { + do { + try buffer.writeLengthPrefixed(as: UInt8.self) { buffer in + buffer.writeString(customAuthType) + } + } catch { + throw Error.invalid(message: "MIME Type \(mimeType) too long to encode") + } + var aux = metadata + buffer.writeBuffer(&aux) } } diff --git a/Sources/RSocketCore/Extensions/Security/AuthenticationType.swift b/Sources/RSocketCore/Extensions/Security/AuthenticationType.swift index 9cff4739..4b563081 100644 --- a/Sources/RSocketCore/Extensions/Security/AuthenticationType.swift +++ b/Sources/RSocketCore/Extensions/Security/AuthenticationType.swift @@ -25,3 +25,53 @@ public extension AuthenticationType { static let simple = Self(rawValue: "simple") static let bearer = Self(rawValue: "bearer") } + + +public enum WellKnownAuthenticationType : Equatable { + case UNPARSEABLE_AUTH_TYPE + case UNKNOWN_RESERVED_AUTH_TYPE + case SIMPLE + case BEARER + + private static var typesByAuthId = [WellKnownAuthenticationType](repeating: .UNKNOWN_RESERVED_AUTH_TYPE, count: 128) + + public static func fromIdentifier(id: Int8) -> WellKnownAuthenticationType { + if id < 0x00 || id > 0x7F { + return .UNPARSEABLE_AUTH_TYPE + } + return typesByAuthId[Int(id)] + } + + public var identifier: UInt8 { + switch self { + case .UNPARSEABLE_AUTH_TYPE : + return UInt8(0xFE) + case .UNKNOWN_RESERVED_AUTH_TYPE: + return UInt8(0xFF) + case .SIMPLE: + return 0x00 + case .BEARER: + return 0x01 + } + } +} + +//public enum WellKnownAuthenticationType { +// case BEARER +// case SIMPLE +// case UNPARSEABLE_AUTH_TYPE +// case UNKNOWN_RESERVED_AUTH_TYPE +// +// public var identifier: UInt8 { +// switch self { +// case .SIMPLE: +// return UInt8(0x00) +// case .BEARER: +// return UInt8(0x01) +// case .UNKNOWN_RESERVED_AUTH_TYPE: +// return UInt8(0xFF) +// case .UNPARSEABLE_AUTH_TYPE: +// return UInt8(0xFE) +// } +// } +//} diff --git a/Sources/RSocketCore/Extensions/Security/Bearer.swift b/Sources/RSocketCore/Extensions/Security/Bearer.swift index f8e65b67..e3cfaafd 100644 --- a/Sources/RSocketCore/Extensions/Security/Bearer.swift +++ b/Sources/RSocketCore/Extensions/Security/Bearer.swift @@ -25,7 +25,14 @@ public struct BearerAuthenticationDecoder: MetadataDecoder { @inlinable public func decode(from buffer: inout ByteBuffer) throws -> String { - fatalError("not implemented") + guard AuthenticationDecoder.isWellKnownAuthType(buffer) else { + throw Error.invalid(message: "invalid bearar metadata") + } + _ = buffer.readBytes(length: 1) + guard let token = buffer.readString(length: buffer.readableBytes) else{ + throw Error.invalid(message: "no token") + } + return token } } @@ -43,7 +50,10 @@ public struct BearerAuthenticationEncoder: MetadataEncoder { @inlinable public func encode(_ metadata: String, into buffer: inout ByteBuffer) throws { - fatalError("not implemented") + buffer = buffer.mergeByteBuffers(buffers:[ + ByteBuffer(integer: UInt8(WellKnownAuthenticationType.BEARER.identifier) | UInt8(0x80)), + ByteBuffer(string: metadata) // Token + ]) } } diff --git a/Sources/RSocketCore/Extensions/Security/Custom.swift b/Sources/RSocketCore/Extensions/Security/Custom.swift new file mode 100644 index 00000000..954232a6 --- /dev/null +++ b/Sources/RSocketCore/Extensions/Security/Custom.swift @@ -0,0 +1,65 @@ +// +// File.swift +// +// +// Created by Elyes Ben Salah on 6/2/2023. +// + + +import NIOCore + + +public struct CustomAuthenticationDecoder: MetadataDecoder { + @usableFromInline + internal var authenticationDecoder: AuthenticationDecoder = .init() + + @inlinable + public var mimeType: MIMEType { authenticationDecoder.mimeType } + + + public func decode(from buffer: inout ByteBuffer) throws -> Authentication { + guard var slice = buffer.readLengthPrefixedSlice(as: UInt8.self), + let customAuthType = slice.readString(length: slice.readableBytes) + else { + throw Error.invalid(message: "authType could not be read") + } + + return .init(type: AuthenticationType(rawValue: customAuthType), payload: ByteBuffer(bytes: buffer.readBytes(length: buffer.readableBytes) ?? [])) + } +} + +extension MetadataDecoder where Self == CustomAuthenticationDecoder { + public static var customAuthentication: Self { .init() } +} + + +public struct CustomAuthenticationEncoder: MetadataEncoder { + @usableFromInline + internal var authenticationEncoder: AuthenticationEncoder = .init() + + @inlinable + public var mimeType: MIMEType { authenticationEncoder.mimeType } + + @inlinable + public func encode(_ metadata: Authentication , into buffer: inout ByteBuffer) throws { + try encodeCustomMetadata(metadata, into: &buffer) + } + + @inlinable + public func encodeCustomMetadata(_ customAuth : Authentication , into buffer: inout ByteBuffer) throws { + do { + try buffer.writeLengthPrefixed(as: UInt8.self) { buffer in + buffer.writeString(customAuth.type.rawValue) + } + } catch { + throw Error.invalid(message: "MIME Type \(mimeType) too long to encode") + } + var aux = customAuth.payload + buffer.writeBuffer(&aux) + } + +} + +extension MetadataEncoder where Self == CustomAuthenticationEncoder { + public static var customAuthentication: Self { .init() } +} diff --git a/Sources/RSocketCore/Extensions/Security/Simple.swift b/Sources/RSocketCore/Extensions/Security/Simple.swift index af217220..be899471 100644 --- a/Sources/RSocketCore/Extensions/Security/Simple.swift +++ b/Sources/RSocketCore/Extensions/Security/Simple.swift @@ -34,7 +34,27 @@ public struct SimpleAuthenticationDecoder: MetadataDecoder { @inlinable public func decode(from buffer: inout ByteBuffer) throws -> SimpleAuthentication { - fatalError("not implemented") + let _ = buffer.readBytes(length: 1)?.withUnsafeBytes{ value -> UInt8 in + return value.load(as: UInt8.self).bigEndian + } + guard var slice = buffer.readLengthPrefixedSlice(as: UInt16.self), + let username = slice.readString(length: slice.readableBytes) + else { + throw Error.invalid(message: "username could not be read") + } + let password = buffer.readString(length: buffer.readableBytes) + return SimpleAuthentication(username: username ,password : password ?? "") + } + + @inlinable + func extractSimpleAuth(username : String, password : String , into buffer : inout ByteBuffer ) { + let usernameLength = UInt16(username.utf8.count) // 2 bits + var bufferLength = ByteBufferAllocator().buffer(capacity: 2) + bufferLength.writeInteger(usernameLength) + buffer = buffer.mergeByteBuffers(buffers: [ByteBuffer(integer: UInt8(WellKnownAuthenticationType.SIMPLE.identifier)), + bufferLength, + ByteBuffer(string: username), + ByteBuffer(string: password)]) } } @@ -52,7 +72,11 @@ public struct SimpleAuthenticationEncoder: MetadataEncoder { @inlinable public func encode(_ metadata: SimpleAuthentication, into buffer: inout ByteBuffer) throws { - fatalError("not implemented") + buffer.writeInteger(UInt8(WellKnownAuthenticationType.SIMPLE.identifier) | UInt8(0x80)) + try buffer.writeLengthPrefixed(as: UInt16.self) { buffer in + try buffer.writeString(metadata.username, encoding: .utf8) + } + try buffer.writeString(metadata.password, encoding: .utf8) } } diff --git a/Sources/RSocketCore/Utility/ByteBuffer+UInt24.swift b/Sources/RSocketCore/Utility/ByteBuffer+UInt24.swift index af0d14c0..02a6ad84 100644 --- a/Sources/RSocketCore/Utility/ByteBuffer+UInt24.swift +++ b/Sources/RSocketCore/Utility/ByteBuffer+UInt24.swift @@ -110,4 +110,15 @@ extension ByteBuffer { moveReaderIndex(forwardBy: 3) return integer } + + @discardableResult + @inlinable + internal mutating func mergeByteBuffers(buffers: [ByteBuffer]) -> ByteBuffer { + let totalLength = buffers.reduce(0){ $0 + $1.readableBytes} + var mergedBuffer = ByteBufferAllocator().buffer(capacity: totalLength) + for buff in buffers { + mergedBuffer.writeBytes(buff.getBytes(at: 0, length: buff.readableBytes) ?? [] ) + } + return mergedBuffer + } } diff --git a/Tests/RSocketCoreTests/Extensions/Security/SecurityTests.swift b/Tests/RSocketCoreTests/Extensions/Security/SecurityTests.swift new file mode 100644 index 00000000..61b20bbc --- /dev/null +++ b/Tests/RSocketCoreTests/Extensions/Security/SecurityTests.swift @@ -0,0 +1,102 @@ +// +// SecurityTests.swift +// +// +// Created by Elyes Ben Salah on 3/2/2023. +// + + +import XCTest +import NIOCore +@testable import RSocketCore + + +final class SecurityTests: XCTestCase { + + + func testTest() throws { + let encoder = AuthenticationEncoder() + let decoder = AuthenticationDecoder() + + var resultEncoder = ByteBuffer() + let authentication = Authentication(type : .init(rawValue: "kindi"), payload: ByteBuffer(string: "someDataForKindiAuth")) + try encoder.encode(authentication , into: &resultEncoder) + let resultDecoder = try decoder.decode(from: &resultEncoder) + + debugPrint(resultDecoder.type) + } + + + func testBearerAuth() throws { + let encoder = BearerAuthenticationEncoder() + let decoder = BearerAuthenticationDecoder() + + var resultEncoder = ByteBuffer() + try encoder.encode("Token",into: &resultEncoder) + let data = resultEncoder.getBytes(at: 0, length: resultEncoder.readableBytes)! + debugPrint(data) + var buffToDecode = ByteBuffer(bytes: data) + let authRz = try decoder.decode(from: &buffToDecode) + + debugPrint("Bearer Auth Decoded \nToken : \(authRz)") + + } + + func testSimpleAuth() throws { + let encoder = SimpleAuthenticationEncoder() + let decoder = SimpleAuthenticationDecoder() + let auth = SimpleAuthentication(username: "LoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefasjdjqfjeqfjnqefkqenkqegknqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkq", password: "Passwordqqn") + var resultEncoder = ByteBuffer() + try encoder.encode(auth,into: &resultEncoder) + debugPrint(resultEncoder.getBytes(at: 0, length: resultEncoder.readableBytes)!) + debugPrint(resultEncoder.getString(at: 0, length: resultEncoder.readableBytes)!) + + let authRz = try decoder.decode(from: &resultEncoder) + debugPrint(authRz) + + } + func testCustomAuth() throws { + let encoder = CustomAuthenticationEncoder() + let decoder = CustomAuthenticationDecoder() + + var resultEncoder = ByteBuffer() + let authentication = Authentication(type : .init(rawValue: "kindi"), payload: ByteBuffer(string: "someDataForKindiAuth")) + + try encoder.encode(authentication,into: &resultEncoder) + let data = resultEncoder.getBytes(at: 0, length: resultEncoder.readableBytes)! + debugPrint(data) + var buffToDecode = ByteBuffer(bytes: data) + var authRz = try decoder.decode(from: &buffToDecode) + + debugPrint("Custom Auth Decoded :\nType : \(authRz.type.rawValue)\nData : \(authRz.payload.readString(length: authRz.payload.readableBytes))") + + } + + + func testWellKnowAuthTypes() throws { + //Simple Auth + let simpleEncoder = SimpleAuthenticationEncoder() + let simpleAuth = SimpleAuthentication(username: "LoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkqegknqegknqefknqefknLoginasjdjqfjeqfjnqefkqenkq", password: "Passwordqqn") + var simpleAuthBuffer = ByteBuffer() + try simpleEncoder.encode(simpleAuth,into: &simpleAuthBuffer) + let resultSimple = AuthenticationDecoder.isWellKnownAuthType(simpleAuthBuffer) + + //Bearer Auth + let bearerEncoder = BearerAuthenticationEncoder() + var bearerAuthBuffer = ByteBuffer() + try bearerEncoder.encode("MyToken",into: &bearerAuthBuffer) + let bearerResult = AuthenticationDecoder.isWellKnownAuthType(bearerAuthBuffer) + + + //Custom Auth + let customEncoder = CustomAuthenticationEncoder() + var customAuthBuffer = ByteBuffer() + let auth = Authentication(type : .init(rawValue: "Kindi"), payload : ByteBuffer(string: "SomeDataForKindiAuth")) + try customEncoder.encodeCustomMetadata(auth, into: &customAuthBuffer) + let customResult = AuthenticationDecoder.isWellKnownAuthType(customAuthBuffer) + + debugPrint("isWellKnown SimpleAuth : \(resultSimple)") + debugPrint("isWellKnown BearerAuth : \(bearerResult)") + debugPrint("isWellKnown CustomAuth : \(!customResult)") + } +}