Skip to content

Commit abd413f

Browse files
authored
feat: Add loginAs method to impersonate users (#79)
* refactor * nits * feat: Add loginAs method to impersonate users * Server should always send back sessionToken * increase codecov * revert * nits * fix become
1 parent b373225 commit abd413f

27 files changed

+500
-164
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@
22
# Parse-Swift Changelog
33

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

8+
### 5.3.0
9+
[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)
10+
11+
__New features__
12+
* 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).
13+
814
### 5.2.0
915
[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)
1016

ParseSwift.playground/Pages/3 - User - Sign Up.xcplaygroundpage/Contents.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,10 @@ struct User: ParseUser {
5252
}
5353
}
5454

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

Sources/ParseSwift/API/API+Command+async.swift

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,15 @@ internal extension API.Command {
4040
try await withCheckedThrowingContinuation { continuation in
4141
Task {
4242
await self.execute(options: options,
43-
batching: batching,
44-
callbackQueue: callbackQueue,
45-
notificationQueue: notificationQueue,
46-
childObjects: childObjects,
47-
childFiles: childFiles,
48-
allowIntermediateResponses: allowIntermediateResponses,
49-
uploadProgress: uploadProgress,
50-
downloadProgress: downloadProgress,
51-
completion: continuation.resume)
43+
batching: batching,
44+
callbackQueue: callbackQueue,
45+
notificationQueue: notificationQueue,
46+
childObjects: childObjects,
47+
childFiles: childFiles,
48+
allowIntermediateResponses: allowIntermediateResponses,
49+
uploadProgress: uploadProgress,
50+
downloadProgress: downloadProgress,
51+
completion: continuation.resume)
5252
}
5353
}
5454
}

Sources/ParseSwift/API/API+NonParseBodyCommand+async.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ extension API.NonParseBodyCommand {
1919
try await withCheckedThrowingContinuation { continuation in
2020
Task {
2121
await self.execute(options: options,
22-
callbackQueue: callbackQueue,
23-
allowIntermediateResponses: allowIntermediateResponses,
24-
completion: continuation.resume)
22+
callbackQueue: callbackQueue,
23+
allowIntermediateResponses: allowIntermediateResponses,
24+
completion: continuation.resume)
2525
}
2626
}
2727
}

Sources/ParseSwift/API/API.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public struct API {
3434
case roles
3535
case role(objectId: String)
3636
case login
37+
case loginAs
3738
case logout
3839
case file(fileName: String)
3940
case passwordReset
@@ -84,6 +85,8 @@ public struct API {
8485
return "/roles/\(objectId)"
8586
case .login:
8687
return "/login"
88+
case .loginAs:
89+
return "/loginAs"
8790
case .logout:
8891
return "/logout"
8992
case .file(let fileName):

Sources/ParseSwift/Objects/ParseInstallation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,7 @@ public extension Sequence where Element: ParseInstallation {
11771177
await API.Command<Self.Element, ParseError?>
11781178
.batch(commands: batch, transaction: transaction)
11791179
.execute(options: options,
1180-
callbackQueue: callbackQueue) { results in
1180+
callbackQueue: callbackQueue) { results in
11811181
switch results {
11821182

11831183
case .success(let saved):

Sources/ParseSwift/Objects/ParseObject.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,7 @@ transactions for this call.
674674
await API.Command<Self.Element, ParseError?>
675675
.batch(commands: batch, transaction: transaction)
676676
.execute(options: immutableOptions,
677-
callbackQueue: callbackQueue) { results in
677+
callbackQueue: callbackQueue) { results in
678678
switch results {
679679

680680
case .success(let saved):
@@ -745,8 +745,8 @@ extension ParseObject {
745745
do {
746746
try await fetchCommand(include: includeKeys)
747747
.execute(options: options,
748-
callbackQueue: callbackQueue,
749-
completion: completion)
748+
callbackQueue: callbackQueue,
749+
completion: completion)
750750
} catch {
751751
let parseError = error as? ParseError ?? ParseError(swift: error)
752752
callbackQueue.async {
@@ -943,7 +943,7 @@ extension ParseObject {
943943
Task {
944944
do {
945945
try await deleteCommand().execute(options: options,
946-
callbackQueue: callbackQueue) { result in
946+
callbackQueue: callbackQueue) { result in
947947
switch result {
948948

949949
case .success:

Sources/ParseSwift/Objects/ParseUser+async.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,36 @@ public extension ParseUser {
125125
}
126126
}
127127

128+
/**
129+
Logs in a `ParseUser` *asynchronously* with a given `objectId` allowing the impersonation of a User.
130+
On success, this saves the logged in `ParseUser`with this session to the keychain, so you can retrieve
131+
the currently logged in user using *current*.
132+
133+
- parameter objectId: The objectId of the user to login.
134+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
135+
- returns: Returns the logged in `ParseUser`.
136+
- throws: An error of type `ParseError`.
137+
- important: The Parse Keychain currently only supports one(1) user at a time. This means
138+
if you use `loginAs()`, the current logged in user will be replaced. If you would like to revert
139+
back to the previous user, you should capture the `sesionToken` of the previous user before
140+
calling `loginAs()`. When you are ready to revert, 1) `logout()`, then `become()` with
141+
the sessionToken.
142+
- note: Calling this endpoint does not invoke session triggers such as beforeLogin and
143+
afterLogin. This action will always succeed if the supplied user exists in the database, regardless
144+
of whether the user is currently locked out.
145+
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
146+
desires a different policy, it should be inserted in `options`.
147+
- requires: `.usePrimaryKey` has to be available. It is recommended to only
148+
use the primary key in server-side applications where the key is kept secure and not
149+
exposed to the public.
150+
*/
151+
@discardableResult static func loginAs(objectId: String,
152+
options: API.Options = []) async throws -> Self {
153+
try await withCheckedThrowingContinuation { continuation in
154+
Self.loginAs(objectId: objectId, options: options, completion: continuation.resume)
155+
}
156+
}
157+
128158
#if !os(Linux) && !os(Android) && !os(Windows)
129159
/**
130160
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.

Sources/ParseSwift/Objects/ParseUser+combine.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,35 @@ public extension ParseUser {
121121
}
122122
}
123123

124+
/**
125+
Logs in a `ParseUser` *asynchronously* with a given `objectId` allowing the impersonation of a User.
126+
On success, this saves the logged in `ParseUser`with this session to the keychain, so you can retrieve
127+
the currently logged in user using *current*.
128+
129+
- parameter objectId: The objectId of the user to login.
130+
- parameter options: A set of header options sent to the server. Defaults to an empty set.
131+
- returns: A publisher that eventually produces a single value and then finishes or fails.
132+
- important: The Parse Keychain currently only supports one(1) user at a time. This means
133+
if you use `loginAs()`, the current logged in user will be replaced. If you would like to revert
134+
back to the previous user, you should capture the `sesionToken` of the previous user before
135+
calling `loginAs()`. When you are ready to revert, 1) `logout()`, then `become()` with
136+
the sessionToken.
137+
- note: Calling this endpoint does not invoke session triggers such as beforeLogin and
138+
afterLogin. This action will always succeed if the supplied user exists in the database, regardless
139+
of whether the user is currently locked out.
140+
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
141+
desires a different policy, it should be inserted in `options`.
142+
- requires: `.usePrimaryKey` has to be available. It is recommended to only
143+
use the primary key in server-side applications where the key is kept secure and not
144+
exposed to the public.
145+
*/
146+
static func loginAsPublisher(objectId: String,
147+
options: API.Options = []) -> Future<Self, ParseError> {
148+
Future { promise in
149+
Self.loginAs(objectId: objectId, options: options, completion: promise)
150+
}
151+
}
152+
124153
#if !os(Linux) && !os(Android) && !os(Windows)
125154
/**
126155
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.

0 commit comments

Comments
 (0)