Skip to content

Commit

Permalink
Add restrictions to create call link API
Browse files Browse the repository at this point in the history
  • Loading branch information
adel-signal authored Jul 25, 2024
1 parent 26e3836 commit 105dc23
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 45 deletions.
7 changes: 5 additions & 2 deletions src/android/api/org/signal/ringrtc/CallManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -982,7 +982,8 @@ public void readCallLink(
* CallLinkSecretParams secretParams = CallLinkSecretParams.deriveFromRootKey(linkKey.getKeyBytes());
* byte[] credentialPresentation = credential.present(roomId, secretParams).serialize();
* byte[] serializedPublicParams = secretParams.getPublicParams().serialize();
* callManager.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams, result -> {
* CallLinkState.Restrictions restrictions = CallLinkState.Restrictions.NONE;
* callManager.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams, restrictions, result -> {
* if (result.isSuccess()) {
* CallLinkState state = result.getValue();
* // In actuality you may not want to do this until the user clicks Done.
Expand Down Expand Up @@ -1016,14 +1017,15 @@ public void createCallLink(
@NonNull CallLinkRootKey linkRootKey,
@NonNull byte[] adminPasskey,
@NonNull byte[] callLinkPublicParams,
@NonNull CallLinkState.Restrictions restrictions,
@NonNull ResponseHandler<HttpResult<CallLinkState>> handler)
throws CallException
{
checkCallManagerExists();
Log.i(TAG, "createCallLink():");

long requestId = this.callLinkRequests.add(handler);
ringrtcCreateCallLink(nativeCallManager, sfuUrl, createCredentialPresentation, linkRootKey.getKeyBytes(), adminPasskey, callLinkPublicParams, requestId);
ringrtcCreateCallLink(nativeCallManager, sfuUrl, createCredentialPresentation, linkRootKey.getKeyBytes(), adminPasskey, callLinkPublicParams, restrictions.ordinal(), requestId);
}

/**
Expand Down Expand Up @@ -2459,6 +2461,7 @@ void ringrtcCreateCallLink(long nativeCallManager,
byte[] rootKeyBytes,
byte[] adminPasskey,
byte[] callLinkPublicParams,
int restrictions,
long requestId)
throws CallException;

Expand Down
4 changes: 2 additions & 2 deletions src/android/src/androidTest/java/CallLinksTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public void testCreateSuccess() throws Exception {
CallManager callManager = CallManager.createCallManager(observer);

CountDownLatch latch = new CountDownLatch(1);
callManager.createCallLink("sfu.example", new byte[] { 1, 2, 3 }, EXAMPLE_KEY, CallLinkRootKey.generateAdminPasskey(), new byte[] { 4, 5, 6 }, result -> {
callManager.createCallLink("sfu.example", new byte[] { 1, 2, 3 }, EXAMPLE_KEY, CallLinkRootKey.generateAdminPasskey(), new byte[] { 4, 5, 6 }, CallLinkState.Restrictions.NONE, result -> {
errors.checkThat(result.getStatus(), is((short)200));
errors.checkThat(result.isSuccess(), is(true));
errors.checkThat(result.getValue().getExpiration().getEpochSecond(), is(EXPIRATION_EPOCH_SECONDS));
Expand All @@ -83,7 +83,7 @@ public void testCreateFailure() throws Exception {
CallManager callManager = CallManager.createCallManager(observer);

CountDownLatch latch = new CountDownLatch(1);
callManager.createCallLink("sfu.example", new byte[] { 1, 2, 3 }, EXAMPLE_KEY, CallLinkRootKey.generateAdminPasskey(), new byte[] { 4, 5, 6 }, result -> {
callManager.createCallLink("sfu.example", new byte[] { 1, 2, 3 }, EXAMPLE_KEY, CallLinkRootKey.generateAdminPasskey(), new byte[] { 4, 5, 6 }, CallLinkState.Restrictions.NONE, result -> {
errors.checkThat(result.getStatus(), is((short)403));
errors.checkThat(result.isSuccess(), is(false));
errors.checkThat(result.getValue(), is((CallLinkState)null));
Expand Down
11 changes: 11 additions & 0 deletions src/ios/SignalRingRTC/SignalRingRTC/CallLinks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,17 @@ public struct CallLinkRootKey: CustomStringConvertible {
public struct CallLinkState {
public enum Restrictions {
case none, adminApproval, unknown

func toOrdinal() -> Int8 {
return switch self {
case .none:
0
case .adminApproval:
1
default:
-1
}
}
}

/// Is never null, but may be empty.
Expand Down
29 changes: 18 additions & 11 deletions src/ios/SignalRingRTC/SignalRingRTC/SFU.swift
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,15 @@ public class SFUClient {
/// let secretParams = CallLinkSecretParams.deriveFromRootKey(linkKey.bytes)
/// let credentialPresentation = credential.present(roomId, secretParams).serialize()
/// let serializedPublicParams = secretParams.getPublicParams().serialize()
/// sfu.createCallLink(sfuUrl: sfuUrl, createCredentialPresentation: credentialPresentation, linkRootKey: linkKey, adminPasskey: adminPasskey, callLinkPublicParams: serializedPublicParams)
/// .done { result in
/// let restrictions = CallLinkState.Restrictions.adminApprovla
/// sfu.createCallLink(
/// sfuUrl: sfuUrl,
/// createCredentialPresentation: credentialPresentation,
/// linkRootKey: linkKey,
/// adminPasskey: adminPasskey,
/// callLinkPublicParams: serializedPublicParams,
/// restrictions: restrictions
/// ).done { result in
/// switch result {
/// case .success(let state):
/// // In actuality you may not want to do this until the user clicks Done.
Expand All @@ -283,8 +290,9 @@ public class SFUClient {
/// - Parameter linkRootKey: the root key for the call link
/// - Parameter adminPasskey: the arbitrary passkey to use for the new room
/// - Parameter callLinkPublicParams: the serialized CallLinkPublicParams for the new room
/// - Parameter restrictions: the restrictions for joining the room. Restrictions.unknown is invalid for creation
@MainActor
public func createCallLink(sfuUrl: String, createCredentialPresentation: [UInt8], linkRootKey: CallLinkRootKey, adminPasskey: Data, callLinkPublicParams: [UInt8]) async -> SFUResult<CallLinkState> {
public func createCallLink(sfuUrl: String, createCredentialPresentation: [UInt8], linkRootKey: CallLinkRootKey, adminPasskey: Data, callLinkPublicParams: [UInt8], restrictions: CallLinkState.Restrictions) async -> SFUResult<CallLinkState> {
return await withCheckedContinuation { continuation in
Logger.debug("createCallLink")

Expand All @@ -294,7 +302,11 @@ public class SFUClient {
linkRootKey.bytes.withRtcBytes { linkRootKey in
adminPasskey.withRtcBytes { adminPasskey in
callLinkPublicParams.withRtcBytes { callLinkPublicParams in
rtc_sfu_createCallLink(self.httpClient.rtcClient, requestId, sfuUrl, createCredentialPresentation, linkRootKey, adminPasskey, callLinkPublicParams, delegateWrapper.asRtc())
let rawRestrictions = restrictions.toOrdinal()
if rawRestrictions < 0 {
preconditionFailure("cannot create call link with restrictions 'unknown'")
}
rtc_sfu_createCallLink(self.httpClient.rtcClient, requestId, sfuUrl, createCredentialPresentation, linkRootKey, adminPasskey, callLinkPublicParams, rawRestrictions, delegateWrapper.asRtc())
}
}
}
Expand Down Expand Up @@ -356,13 +368,8 @@ public class SFUClient {
authCredentialPresentation.withRtcBytes { createCredentialPresentation in
linkRootKey.bytes.withRtcBytes { linkRootKey in
adminPasskey.withRtcBytes { adminPasskey in
let rawRestrictions: Int8
switch restrictions {
case .none:
rawRestrictions = 0
case .adminApproval:
rawRestrictions = 1
default:
let rawRestrictions = restrictions.toOrdinal()
if rawRestrictions < 0 {
preconditionFailure("cannot update restrictions to 'unknown'")
}
rtc_sfu_updateCallLink(self.httpClient.rtcClient, requestId, sfuUrl, createCredentialPresentation, linkRootKey, adminPasskey, nil, rawRestrictions, -1, delegateWrapper.asRtc())
Expand Down
4 changes: 2 additions & 2 deletions src/ios/SignalRingRTC/SignalRingRTCTests/CallLinkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class CallLinkTests: XCTestCase {
httpClient.receivedResponse(requestId: id, response: HTTPResponse(statusCode: 200, body: Self.EXAMPLE_STATE_JSON.data(using: .utf8)))
}

let result = await sfu.createCallLink(sfuUrl: "sfu.example", createCredentialPresentation: [1, 2, 3], linkRootKey: Self.EXAMPLE_KEY, adminPasskey: CallLinkRootKey.generateAdminPasskey(), callLinkPublicParams: [4, 5, 6])
let result = await sfu.createCallLink(sfuUrl: "sfu.example", createCredentialPresentation: [1, 2, 3], linkRootKey: Self.EXAMPLE_KEY, adminPasskey: CallLinkRootKey.generateAdminPasskey(), callLinkPublicParams: [4, 5, 6], restrictions: .none)
switch result {
case .success(let state):
XCTAssertEqual(state.expiration.timeIntervalSince1970, Self.EXPIRATION_EPOCH_SECONDS)
Expand All @@ -55,7 +55,7 @@ final class CallLinkTests: XCTestCase {
httpClient.receivedResponse(requestId: id, response: HTTPResponse(statusCode: 403, body: Data()))
}

let result = await sfu.createCallLink(sfuUrl: "sfu.example", createCredentialPresentation: [1, 2, 3], linkRootKey: Self.EXAMPLE_KEY, adminPasskey: CallLinkRootKey.generateAdminPasskey(), callLinkPublicParams: [4, 5, 6])
let result = await sfu.createCallLink(sfuUrl: "sfu.example", createCredentialPresentation: [1, 2, 3], linkRootKey: Self.EXAMPLE_KEY, adminPasskey: CallLinkRootKey.generateAdminPasskey(), callLinkPublicParams: [4, 5, 6], restrictions: .adminApproval)
switch result {
case .success(let state):
XCTFail("unexpected success: \(state)")
Expand Down
12 changes: 8 additions & 4 deletions src/node/ringrtc/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,8 @@ export class RingRTCType {
* const secretParams = CallLinkSecretParams.deriveFromRootKey(linkKey.bytes);
* const credentialPresentation = credential.present(roomId, secretParams).serialize();
* const serializedPublicParams = secretParams.getPublicParams().serialize();
* const result = await RingRTC.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams);
* const restrictions = CallLinkState.Restrictions.None;
* const result = await RingRTC.createCallLink(sfuUrl, credentialPresentation, linkKey, adminPasskey, serializedPublicParams, restrictions);
* if (result.success) {
* const state = result.value;
* // In actuality you may not want to do this until the user clicks Done.
Expand All @@ -963,7 +964,8 @@ export class RingRTCType {
createCredentialPresentation: Buffer,
linkRootKey: CallLinkRootKey,
adminPasskey: Buffer,
callLinkPublicParams: Buffer
callLinkPublicParams: Buffer,
restrictions: Exclude<CallLinkRestrictions, CallLinkRestrictions.Unknown>
): Promise<HttpResult<CallLinkState>> {
const [requestId, promise] = this._callLinkRequests.add();
// Response comes back via handleCallLinkResponse
Expand All @@ -974,7 +976,8 @@ export class RingRTCType {
createCredentialPresentation,
linkRootKey.bytes,
adminPasskey,
callLinkPublicParams
callLinkPublicParams,
restrictions
);
});
return promise;
Expand Down Expand Up @@ -2959,7 +2962,8 @@ export interface CallManager {
createCredentialPresentation: Buffer,
linkRootKey: Buffer,
adminPasskey: Buffer,
callLinkPublicParams: Buffer
callLinkPublicParams: Buffer,
restrictions: number | undefined
): void;
updateCallLink(
requestId: number,
Expand Down
6 changes: 4 additions & 2 deletions src/node/test/RingRTC-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@ describe('RingRTC', () => {
Buffer.of(1, 2, 3),
EXAMPLE_KEY,
CallLinkRootKey.generateAdminPassKey(),
Buffer.of(4, 5, 6)
Buffer.of(4, 5, 6),
CallLinkRestrictions.None
);
const requestId = await requestIdPromise;
RingRTC.receivedHttpResponse(
Expand Down Expand Up @@ -521,7 +522,8 @@ describe('RingRTC', () => {
Buffer.of(1, 2, 3),
EXAMPLE_KEY,
CallLinkRootKey.generateAdminPassKey(),
Buffer.of(4, 5, 6)
Buffer.of(4, 5, 6),
CallLinkRestrictions.None
);
const requestId = await requestIdPromise;
RingRTC.receivedHttpResponse(requestId, 403, Buffer.of());
Expand Down
2 changes: 2 additions & 0 deletions src/rust/src/android/api/jni_call_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ pub unsafe extern "C" fn Java_org_signal_ringrtc_CallManager_ringrtcCreateCallLi
root_key: JByteArray,
admin_passkey: JByteArray,
call_link_public_params: JByteArray,
restrictions: jint,
request_id: jlong,
) {
match call_manager::create_call_link(
Expand All @@ -624,6 +625,7 @@ pub unsafe extern "C" fn Java_org_signal_ringrtc_CallManager_ringrtcCreateCallLi
root_key,
admin_passkey,
call_link_public_params,
restrictions,
request_id,
) {
Ok(v) => v,
Expand Down
17 changes: 12 additions & 5 deletions src/rust/src/android/call_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,7 @@ pub fn create_call_link(
root_key: JByteArray,
admin_passkey: JByteArray,
call_link_public_params: JByteArray,
restrictions: jint,
request_id: jlong,
) -> Result<()> {
let sfu_url = env.get_string(&sfu_url)?;
Expand All @@ -569,6 +570,7 @@ pub fn create_call_link(
call_links::CallLinkRootKey::try_from(env.convert_byte_array(root_key)?.as_slice())?;
let admin_passkey = env.convert_byte_array(admin_passkey)?;
let call_link_public_params = env.convert_byte_array(call_link_public_params)?;
let restrictions = jint_to_restrictions(restrictions);

let call_manager = unsafe { ptr_as_mut(call_manager)? };
let platform = call_manager.platform()?.try_clone()?;
Expand All @@ -579,6 +581,7 @@ pub fn create_call_link(
&create_credential_presentation,
&admin_passkey,
&call_link_public_params,
restrictions,
Box::new(move |result| {
platform.handle_call_link_result(request_id as u32, result);
}),
Expand Down Expand Up @@ -618,11 +621,7 @@ pub fn update_call_link(
root_key.encrypt(name.as_bytes(), rand::rngs::OsRng)
}
});
let new_restrictions = match new_restrictions {
0 => Some(CallLinkRestrictions::None),
1 => Some(CallLinkRestrictions::AdminApproval),
_ => None,
};
let new_restrictions = jint_to_restrictions(new_restrictions);
let new_revoked = match new_revoked {
0 => Some(false),
1 => Some(true),
Expand Down Expand Up @@ -1162,3 +1161,11 @@ pub fn raise_hand(
call_manager.raise_hand(client_id, raise);
Ok(())
}

fn jint_to_restrictions(raw_restrictions: jint) -> Option<CallLinkRestrictions> {
match raw_restrictions {
0 => Some(CallLinkRestrictions::None),
1 => Some(CallLinkRestrictions::AdminApproval),
_ => None,
}
}
1 change: 1 addition & 0 deletions src/rust/src/bin/call_link.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ The admin passkey for any created links is a constant {ADMIN_PASSKEY:?}.
&bincode::serialize(&create_credential_presentation).unwrap(),
ADMIN_PASSKEY,
&bincode::serialize(&call_link_zkparams.get_public_params()).unwrap(),
None,
Box::new(show_result),
);
}
Expand Down
34 changes: 22 additions & 12 deletions src/rust/src/electron.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1903,6 +1903,24 @@ fn readCallLink(mut cx: FunctionContext) -> JsResult<JsValue> {
Ok(cx.undefined().upcast())
}

fn jsvalue_to_restrictions(
raw_restrictions: Handle<'_, JsValue>,
cx: &mut FunctionContext,
) -> std::result::Result<Option<CallLinkRestrictions>, neon::result::Throw> {
if raw_restrictions.is_a::<JsUndefined, _>(cx) {
Ok(None)
} else {
let raw_restrictions = raw_restrictions
.downcast_or_throw::<JsNumber, _>(cx)?
.value(cx);
Ok(match raw_restrictions as i8 {
0 => Some(CallLinkRestrictions::None),
1 => Some(CallLinkRestrictions::AdminApproval),
_ => None,
})
}
}

#[allow(non_snake_case)]
fn createCallLink(mut cx: FunctionContext) -> JsResult<JsValue> {
let request_id = cx.argument::<JsNumber>(0)?.value(&mut cx) as u32;
Expand All @@ -1916,6 +1934,8 @@ fn createCallLink(mut cx: FunctionContext) -> JsResult<JsValue> {
let admin_passkey = admin_passkey.as_slice(&cx).to_vec();
let public_zkparams = cx.argument::<JsBuffer>(5)?;
let public_zkparams = public_zkparams.as_slice(&cx).to_vec();
let restrictions = cx.argument::<JsValue>(6)?;
let restrictions = jsvalue_to_restrictions(restrictions, &mut cx)?;

with_call_endpoint(&mut cx, |endpoint| {
let event_reporter = endpoint.event_reporter.clone();
Expand All @@ -1926,6 +1946,7 @@ fn createCallLink(mut cx: FunctionContext) -> JsResult<JsValue> {
&create_presentation,
&admin_passkey,
&public_zkparams,
restrictions,
Box::new(move |result| {
// Ignore errors, that can only mean we're shutting down.
let _ = event_reporter.send(Event::CallLinkResponse { request_id, result });
Expand Down Expand Up @@ -1964,18 +1985,7 @@ fn updateCallLink(mut cx: FunctionContext) -> JsResult<JsValue> {
};

let new_restrictions = cx.argument::<JsValue>(6)?;
let new_restrictions = if new_restrictions.is_a::<JsUndefined, _>(&mut cx) {
None
} else {
let raw_restrictions = new_restrictions
.downcast_or_throw::<JsNumber, _>(&mut cx)?
.value(&mut cx);
match raw_restrictions as i8 {
0 => Some(CallLinkRestrictions::None),
1 => Some(CallLinkRestrictions::AdminApproval),
_ => None,
}
};
let new_restrictions = jsvalue_to_restrictions(new_restrictions, &mut cx)?;

let new_revoked = cx.argument::<JsValue>(7)?;
let new_revoked = if new_revoked.is_a::<JsUndefined, _>(&mut cx) {
Expand Down
Loading

0 comments on commit 105dc23

Please sign in to comment.