Skip to content

Commit

Permalink
Merge pull request #395 from ForgeRock/SDKS-545
Browse files Browse the repository at this point in the history
SDKS 545 - TextInputCallback Support
  • Loading branch information
spetrov authored Feb 20, 2024
2 parents 7a98174 + 02f11b5 commit 1b152ac
Show file tree
Hide file tree
Showing 10 changed files with 377 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 - 2023 ForgeRock. All rights reserved.
* Copyright (c) 2019 - 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -66,6 +66,7 @@ private CallbackFragmentFactory() {
register(WebAuthnAuthenticationCallback.class, WebAuthnAuthenticationCallbackFragment.class);
register(SelectIdPCallback.class, SelectIdPCallbackFragment.class);
register(IdPCallback.class, IdPCallbackFragment.class);
register(TextInputCallback.class, TextInputCallbackFragment.class);
}

public static CallbackFragmentFactory getInstance() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright (c) 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

package org.forgerock.android.auth.ui.callback;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;

import com.google.android.material.textfield.TextInputLayout;

import org.forgerock.android.auth.callback.TextInputCallback;
import org.forgerock.android.auth.ui.R;

/**
* UI representation for {@link TextInputCallback}
*/
public class TextInputCallbackFragment extends CallbackFragment<TextInputCallback> {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
final View view = inflater.inflate(R.layout.fragment_text_input_callback, container, false);

EditText text = view.findViewById(R.id.text);
if (callback.getDefaultText() != null) {
text.setText(callback.getDefaultText().toString());
}
TextInputLayout textInputLayout = view.findViewById(R.id.textInputLayout);
textInputLayout.setHint(callback.getPrompt());
text.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {

}

@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {

}

@Override
public void afterTextChanged(Editable s) {
callback.setValue(s.toString());
onDataCollected();
}
});
return view;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2024 ForgeRock. All rights reserved.
~
~ This software may be modified and distributed under the terms
~ of the MIT license. See the LICENSE file for details.
-->

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".callback.TextInputCallbackFragment" android:id="@+id/frameLayout">

<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/textInputLayout"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:passwordToggleEnabled="true">

<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/text"
android:inputType="text"/>
</com.google.android.material.textfield.TextInputLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright (c) 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

package org.forgerock.android.auth.callback;

import static org.assertj.core.api.Assertions.assertThat;

import org.forgerock.android.auth.AndroidBaseTest;
import org.forgerock.android.auth.FRSession;
import org.forgerock.android.auth.Node;
import org.forgerock.android.auth.NodeListener;
import org.forgerock.android.auth.NodeListenerFuture;
import org.forgerock.android.auth.UsernamePasswordNodeListener;
import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.ExecutionException;

public class TextInputCallbackTest extends AndroidBaseTest {
protected final static String TREE = "TextInputCallbackTest";

@Test
public void testTextInputCallback() throws ExecutionException, InterruptedException {
final int[] textInputCallbackReceived = {0};
final int[] success = {0};
NodeListenerFuture<FRSession> nodeListenerFuture = new UsernamePasswordNodeListener(context) {
final NodeListener<FRSession> nodeListener = this;

@Override
public void onCallbackReceived(Node node) {
if (node.getCallback(NameCallback.class) != null) {
NameCallback nameCallback = node.getCallback(NameCallback.class);
nameCallback.setValue(USERNAME);
node.next(context, this );
return;
}
if (node.getCallback(TextInputCallback.class) != null) {
TextInputCallback callback = node.getCallback(TextInputCallback.class);
assertThat(callback.getPrompt()).isEqualTo("What is your username?");
assertThat(callback.getDefaultText()).isEqualTo("ForgerRocker");
textInputCallbackReceived[0]++;
callback.setValue(USERNAME);
node.next(context, nodeListener);
return;
}
// This step here is to ensure that the SDK correctly sets the value in the TextInputCallback...
// The values entered in the NameCallback and TextInputCallback above should match for "success"
if (node.getCallback(TextOutputCallback.class) != null) {
TextOutputCallback callback = node.getCallback(TextOutputCallback.class);
assertThat(callback.getMessage()).isEqualTo("Success");
success[0]++;
node.next(context, nodeListener);
return;
}
super.onCallbackReceived(node);
}
};

FRSession.authenticate(context, TREE, nodeListenerFuture);
Assert.assertNotNull(nodeListenerFuture.get());
assertThat(textInputCallbackReceived[0]).isEqualTo(1);
assertThat(success[0]).isEqualTo(1);

// Ensure that the journey finishes with success
Assert.assertNotNull(FRSession.getCurrentSession());
Assert.assertNotNull(FRSession.getCurrentSession().getSessionToken());
}
}
6 changes: 3 additions & 3 deletions forgerock-auth/src/androidTest/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
<string name="forgerock_oauth_client_id" translatable="false">AndroidTest</string>
<string name="forgerock_oauth_redirect_uri" translatable="false">org.forgerock.demo:/oauth2redirect</string>
<string name="forgerock_oauth_scope" translatable="false">openid profile email address phone</string>
<string name="forgerock_oauth_url" translatable="false">https://openam-forgerrock-sdksteanant.forgeblocks.com/am</string>
<string name="forgerock_oauth_url" translatable="false">https://openam-sdks.forgeblocks.com/am</string>
<integer name="forgerock_oauth_threshold" translatable="false">30</integer> <!-- in second -->

<!-- The following setting is for local development environment only -->
<!-- Server -->
<string name="forgerock_url" translatable="false">https://openam-forgerrock-sdksteanant.forgeblocks.com/am</string>
<string name="forgerock_url" translatable="false">https://openam-sdks.forgeblocks.com/am</string>
<string name="forgerock_realm" translatable="false">alpha</string>
<integer name="forgerock_timeout" translatable="false">30</integer>
<string name="forgerock_cookie_name" translatable="false">iPlanetDirectoryPro</string>
<string name="forgerock_cookie_name" translatable="false">5421aeddf91aa20</string>

<!-- Service -->
<string name="forgerock_auth_service" translatable="false">NamePasswordCallbackTest</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019 - 2023 ForgeRock. All rights reserved.
* Copyright (c) 2019 - 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
Expand Down Expand Up @@ -49,6 +49,7 @@ private CallbackFactory() {
register(DeviceBindingCallback.class);
register(DeviceSigningVerifierCallback.class);
register(AppIntegrityCallback.class);
register(TextInputCallback.class);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

package org.forgerock.android.auth.callback

import androidx.annotation.Keep
import org.json.JSONObject

/**
* Callback for collection of a single text input attribute from a user.
*
*
*/
class TextInputCallback : AbstractPromptCallback {

/**
* TextInputCallback sample.
*
* {
* "type": "TextInputCallback",
* "output": [
* {
* "name": "prompt",
* "value": "Text input"
* },
* {
* "name": "defaultText",
* "value": ""
* }
* ],
* "input": [
* {
* "name": "IDToken1",
* "value": ""
* }
* ]
* }
*
*/

/**
* The text to be used as the default text displayed with the prompt.
*/
var defaultText: String? = null
private set

@Keep
@JvmOverloads
constructor() : super()

@Keep
@JvmOverloads
constructor(raw: JSONObject?, index: Int) : super(raw, index)

override fun setAttribute(name: String, value: Any) {
super.setAttribute(name, value)
when (name) {
"defaultText" -> defaultText = value as String
else -> {}
}
}

/**
* Set the text.
* @param value the text, which may be null.
*/
fun setValue(value: String?) {
super.setValue(value)
}

override fun getType(): String {
return "TextInputCallback"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright (c) 2024 ForgeRock. All rights reserved.
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/

package org.forgerock.android.auth.callback

import androidx.test.ext.junit.runners.AndroidJUnit4
import org.json.JSONException
import org.json.JSONObject
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class TextInputCallbackTest {
@Test
@Throws(JSONException::class)
fun basicTest() {
val raw = JSONObject("""{
"type": "TextInputCallback",
"output": [
{
"name": "prompt",
"value": "One Time Pin"
},
{
"name": "defaultText",
"value": ""
}
],
"input": [
{
"name": "IDToken1",
"value": ""
}
],
"_id": 0
}""")
val textInputCallback = TextInputCallback(raw, 0)
Assert.assertEquals("One Time Pin", textInputCallback.getPrompt())
Assert.assertEquals("", textInputCallback.defaultText)
textInputCallback.setValue("010101")
Assert.assertEquals((textInputCallback.contentAsJson.getJSONArray("input")[0] as JSONObject).getString(
"value"),
"010101")
Assert.assertEquals(0, textInputCallback.get_id().toLong())
}
}
Loading

0 comments on commit 1b152ac

Please sign in to comment.