Skip to content

Commit

Permalink
feat: Add loginAs method to impersonate users (#79)
Browse files Browse the repository at this point in the history
* refactor

* nits

* feat: Add loginAs method to impersonate users

* Server should always send back sessionToken

* increase codecov

* revert

* nits

* fix become
  • Loading branch information
cbaker6 authored Mar 15, 2023
1 parent b373225 commit abd413f
Show file tree
Hide file tree
Showing 27 changed files with 500 additions and 164 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
# Parse-Swift Changelog

### main
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.2.0...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.3.0...main), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/main/documentation/parseswift)
* _Contributing to this repo? Add info about your change here to be included in the next release_

### 5.3.0
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.2.0...5.3.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.3.0/documentation/parseswift)

__New features__
* Add ParseUser.loginAs(objectId: String) method to allow impersonating a user. This method requires the server primaryKey and is intended to run server-side ([#79](https://github.com/netreconlab/Parse-Swift/pull/79)), thanks to [Corey Baker](https://github.com/cbaker6).

### 5.2.0
[Full Changelog](https://github.com/netreconlab/Parse-Swift/compare/5.1.1...5.2.0), [Documentation](https://swiftpackageindex.com/netreconlab/Parse-Swift/5.2.0/documentation/parseswift)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ struct User: ParseUser {
}
}

/*: Sign up user asynchronously - Performs work on background
queue and returns to specified callbackQueue.
If no callbackQueue is specified it returns to main queue.
/*:
Sign up user asynchronously - Performs work on background
queue and returns to specified callbackQueue.
If no callbackQueue is specified it returns to main queue.
*/
User.signup(username: "hello", password: "TestMePass123^") { results in

Expand Down
18 changes: 9 additions & 9 deletions Sources/ParseSwift/API/API+Command+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ internal extension API.Command {
try await withCheckedThrowingContinuation { continuation in
Task {
await self.execute(options: options,
batching: batching,
callbackQueue: callbackQueue,
notificationQueue: notificationQueue,
childObjects: childObjects,
childFiles: childFiles,
allowIntermediateResponses: allowIntermediateResponses,
uploadProgress: uploadProgress,
downloadProgress: downloadProgress,
completion: continuation.resume)
batching: batching,
callbackQueue: callbackQueue,
notificationQueue: notificationQueue,
childObjects: childObjects,
childFiles: childFiles,
allowIntermediateResponses: allowIntermediateResponses,
uploadProgress: uploadProgress,
downloadProgress: downloadProgress,
completion: continuation.resume)
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/ParseSwift/API/API+NonParseBodyCommand+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ extension API.NonParseBodyCommand {
try await withCheckedThrowingContinuation { continuation in
Task {
await self.execute(options: options,
callbackQueue: callbackQueue,
allowIntermediateResponses: allowIntermediateResponses,
completion: continuation.resume)
callbackQueue: callbackQueue,
allowIntermediateResponses: allowIntermediateResponses,
completion: continuation.resume)
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions Sources/ParseSwift/API/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public struct API {
case roles
case role(objectId: String)
case login
case loginAs
case logout
case file(fileName: String)
case passwordReset
Expand Down Expand Up @@ -84,6 +85,8 @@ public struct API {
return "/roles/\(objectId)"
case .login:
return "/login"
case .loginAs:
return "/loginAs"
case .logout:
return "/logout"
case .file(let fileName):
Expand Down
2 changes: 1 addition & 1 deletion Sources/ParseSwift/Objects/ParseInstallation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1177,7 +1177,7 @@ public extension Sequence where Element: ParseInstallation {
await API.Command<Self.Element, ParseError?>
.batch(commands: batch, transaction: transaction)
.execute(options: options,
callbackQueue: callbackQueue) { results in
callbackQueue: callbackQueue) { results in
switch results {

case .success(let saved):
Expand Down
8 changes: 4 additions & 4 deletions Sources/ParseSwift/Objects/ParseObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -674,7 +674,7 @@ transactions for this call.
await API.Command<Self.Element, ParseError?>
.batch(commands: batch, transaction: transaction)
.execute(options: immutableOptions,
callbackQueue: callbackQueue) { results in
callbackQueue: callbackQueue) { results in
switch results {

case .success(let saved):
Expand Down Expand Up @@ -745,8 +745,8 @@ extension ParseObject {
do {
try await fetchCommand(include: includeKeys)
.execute(options: options,
callbackQueue: callbackQueue,
completion: completion)
callbackQueue: callbackQueue,
completion: completion)
} catch {
let parseError = error as? ParseError ?? ParseError(swift: error)
callbackQueue.async {
Expand Down Expand Up @@ -943,7 +943,7 @@ extension ParseObject {
Task {
do {
try await deleteCommand().execute(options: options,
callbackQueue: callbackQueue) { result in
callbackQueue: callbackQueue) { result in
switch result {

case .success:
Expand Down
30 changes: 30 additions & 0 deletions Sources/ParseSwift/Objects/ParseUser+async.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,36 @@ public extension ParseUser {
}
}

/**
Logs in a `ParseUser` *asynchronously* with a given `objectId` allowing the impersonation of a User.
On success, this saves the logged in `ParseUser`with this session to the keychain, so you can retrieve
the currently logged in user using *current*.
- parameter objectId: The objectId of the user to login.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: Returns the logged in `ParseUser`.
- throws: An error of type `ParseError`.
- important: The Parse Keychain currently only supports one(1) user at a time. This means
if you use `loginAs()`, the current logged in user will be replaced. If you would like to revert
back to the previous user, you should capture the `sesionToken` of the previous user before
calling `loginAs()`. When you are ready to revert, 1) `logout()`, then `become()` with
the sessionToken.
- note: Calling this endpoint does not invoke session triggers such as beforeLogin and
afterLogin. This action will always succeed if the supplied user exists in the database, regardless
of whether the user is currently locked out.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
- requires: `.usePrimaryKey` has to be available. It is recommended to only
use the primary key in server-side applications where the key is kept secure and not
exposed to the public.
*/
@discardableResult static func loginAs(objectId: String,
options: API.Options = []) async throws -> Self {
try await withCheckedThrowingContinuation { continuation in
Self.loginAs(objectId: objectId, options: options, completion: continuation.resume)
}
}

#if !os(Linux) && !os(Android) && !os(Windows)
/**
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.
Expand Down
29 changes: 29 additions & 0 deletions Sources/ParseSwift/Objects/ParseUser+combine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,35 @@ public extension ParseUser {
}
}

/**
Logs in a `ParseUser` *asynchronously* with a given `objectId` allowing the impersonation of a User.
On success, this saves the logged in `ParseUser`with this session to the keychain, so you can retrieve
the currently logged in user using *current*.
- parameter objectId: The objectId of the user to login.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- important: The Parse Keychain currently only supports one(1) user at a time. This means
if you use `loginAs()`, the current logged in user will be replaced. If you would like to revert
back to the previous user, you should capture the `sesionToken` of the previous user before
calling `loginAs()`. When you are ready to revert, 1) `logout()`, then `become()` with
the sessionToken.
- note: Calling this endpoint does not invoke session triggers such as beforeLogin and
afterLogin. This action will always succeed if the supplied user exists in the database, regardless
of whether the user is currently locked out.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
- requires: `.usePrimaryKey` has to be available. It is recommended to only
use the primary key in server-side applications where the key is kept secure and not
exposed to the public.
*/
static func loginAsPublisher(objectId: String,
options: API.Options = []) -> Future<Self, ParseError> {
Future { promise in
Self.loginAs(objectId: objectId, options: options, completion: promise)
}
}

#if !os(Linux) && !os(Android) && !os(Windows)
/**
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.
Expand Down
Loading

0 comments on commit abd413f

Please sign in to comment.