您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
399 行
14 KiB
399 行
14 KiB
using System;
|
|
using System.Collections.Generic;
|
|
using Unity.Services.Authentication.Editor.Models;
|
|
using Unity.Services.Core.Internal;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using UnityEngine.UIElements;
|
|
|
|
namespace Unity.Services.Authentication.Editor
|
|
{
|
|
class IdProviderOptions
|
|
{
|
|
public const string IdProviderApple = "apple.com";
|
|
public const string IdProviderGoogle = "google.com";
|
|
public const string IdProviderFacebook = "facebook.com";
|
|
public const string IdProviderSteam = "steampowered.com";
|
|
|
|
public string IdProviderType { get; set; }
|
|
public string DisplayName { get; set; }
|
|
public string ClientIdDisplayName { get; set; } = "Client ID";
|
|
|
|
public string ClientSecretDisplayName { get; set; } = "Client Secret";
|
|
public bool NeedClientSecret {get; set; }
|
|
|
|
static readonly Dictionary<string, IdProviderOptions> s_IdProviderOptions = new Dictionary<string, IdProviderOptions>
|
|
{
|
|
[IdProviderApple] = new IdProviderOptions
|
|
{
|
|
IdProviderType = IdProviderApple,
|
|
DisplayName = "Sign-in with Apple",
|
|
ClientIdDisplayName = "App ID",
|
|
NeedClientSecret = false
|
|
},
|
|
[IdProviderGoogle] = new IdProviderOptions
|
|
{
|
|
IdProviderType = IdProviderGoogle,
|
|
DisplayName = "Google",
|
|
ClientIdDisplayName = "Client ID",
|
|
NeedClientSecret = false
|
|
},
|
|
[IdProviderFacebook] = new IdProviderOptions
|
|
{
|
|
IdProviderType = IdProviderFacebook,
|
|
DisplayName = "Facebook",
|
|
ClientIdDisplayName = "App ID",
|
|
ClientSecretDisplayName = "App Secret",
|
|
NeedClientSecret = true
|
|
},
|
|
[IdProviderSteam] = new IdProviderOptions
|
|
{
|
|
IdProviderType = IdProviderSteam,
|
|
DisplayName = "Steam",
|
|
ClientIdDisplayName = "App ID",
|
|
ClientSecretDisplayName = "Key",
|
|
NeedClientSecret = true
|
|
}
|
|
};
|
|
|
|
public static IdProviderOptions GetOptions(string idProviderType)
|
|
{
|
|
if (!s_IdProviderOptions.ContainsKey(idProviderType))
|
|
{
|
|
return null;
|
|
}
|
|
return s_IdProviderOptions[idProviderType];
|
|
}
|
|
}
|
|
|
|
class IdProviderElement : VisualElement
|
|
{
|
|
const string k_ElementUxml = "Packages/com.unity.services.authentication/Editor/UXML/IdProviderElement.uxml";
|
|
|
|
string m_IdDomainId;
|
|
IAuthenticationAdminClient m_AdminClient;
|
|
IdProviderOptions m_Options;
|
|
|
|
Foldout m_Container;
|
|
Toggle m_Enabled;
|
|
TextField m_ClientId;
|
|
TextField m_ClientSecret;
|
|
Button m_SaveButton;
|
|
Button m_CancelButton;
|
|
Button m_DeleteButton;
|
|
|
|
// Whether skip the confirmation window for tests/automation.
|
|
bool m_SkipConfirmation;
|
|
|
|
/// <summary>
|
|
/// The foldout container to show or hide the ID provider details.
|
|
/// </summary>
|
|
public Foldout Container => m_Container;
|
|
|
|
/// <summary>
|
|
/// The toggle to control whether the ID provider is enabled.
|
|
/// </summary>
|
|
public Toggle EnabledToggle => m_Enabled;
|
|
|
|
/// <summary>
|
|
/// The text field to fill the client ID.
|
|
/// </summary>
|
|
public TextField ClientIdField => m_ClientId;
|
|
|
|
/// <summary>
|
|
/// The text field to fill the client secret.
|
|
/// </summary>
|
|
public TextField ClientSecretField => m_ClientSecret;
|
|
|
|
/// <summary>
|
|
/// The button to save the changes.
|
|
/// </summary>
|
|
public Button SaveButton => m_SaveButton;
|
|
|
|
/// <summary>
|
|
/// The button to cancel changes.
|
|
/// </summary>
|
|
public Button CancelButton => m_CancelButton;
|
|
|
|
|
|
/// <summary>
|
|
/// The button to delete the current ID provider.
|
|
/// </summary>
|
|
public Button DeleteButton => m_DeleteButton;
|
|
|
|
/// <summary>
|
|
/// Event triggered when the <cref="IdProviderElement"/> starts or finishes waiting for an async operation.
|
|
/// The first parameter of the callback is the sender.
|
|
/// The second parameter is true if it starts waiting, and false if it finishes waiting.
|
|
/// </summary>
|
|
public event Action<IdProviderElement, bool> Waiting;
|
|
|
|
/// <summary>
|
|
/// Event triggered when the current <cref="IdProviderElement"/> needs to be deleted by the container.
|
|
/// The parameter of the callback is the sender.
|
|
/// </summary>
|
|
public event Action<IdProviderElement> Deleted;
|
|
|
|
/// <summary>
|
|
/// Event triggered when the current <cref="IdProviderElement"/> catches an error.
|
|
/// The first parameter of the callback is the sender.
|
|
/// The second parameter is the exception caught by the element.
|
|
/// </summary>
|
|
public event Action<IdProviderElement, Exception> Error;
|
|
|
|
/// <summary>
|
|
/// The value saved on the server side.
|
|
/// </summary>
|
|
public IdProviderResponse SavedValue { get; set; }
|
|
|
|
/// <summary>
|
|
/// The value of that is about to be saved to the server.
|
|
/// </summary>
|
|
public IdProviderResponse CurrentValue { get; set; }
|
|
|
|
public bool Changed =>
|
|
SavedValue?.Type != CurrentValue?.Type ||
|
|
SavedValue?.Disabled != CurrentValue?.Disabled ||
|
|
(SavedValue?.ClientId ?? "") != (CurrentValue?.ClientId ?? "") ||
|
|
(SavedValue?.ClientSecret ?? "") != (CurrentValue?.ClientSecret ?? "");
|
|
|
|
public bool IsValid =>
|
|
!string.IsNullOrEmpty(CurrentValue.ClientId) &&
|
|
(!m_Options.NeedClientSecret || !string.IsNullOrEmpty(CurrentValue.ClientSecret));
|
|
|
|
public IdProviderElement(string idDomain, IAuthenticationAdminClient adminClient, IdProviderResponse savedValue, IdProviderOptions options, bool skipConfirmation = false)
|
|
{
|
|
m_IdDomainId = idDomain;
|
|
m_AdminClient = adminClient;
|
|
m_Options = options;
|
|
m_SkipConfirmation = skipConfirmation;
|
|
|
|
SavedValue = savedValue;
|
|
CurrentValue = SavedValue.Clone();
|
|
|
|
var containerAsset = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>(k_ElementUxml);
|
|
if (containerAsset != null)
|
|
{
|
|
var containerUI = containerAsset.CloneTree().contentContainer;
|
|
|
|
m_Container = containerUI.Q<Foldout>(className: "auth-id-provider-details");
|
|
m_Container.text = savedValue.Type;
|
|
|
|
// If the ID Provider element is new, default to unfold.
|
|
m_Container.value = SavedValue.New;
|
|
|
|
m_Enabled = containerUI.Q<Toggle>("id-provider-enabled");
|
|
m_Enabled.RegisterCallback<ChangeEvent<bool>>(OnEnabledChanged);
|
|
|
|
m_ClientId = containerUI.Q<TextField>("id-provider-client-id");
|
|
m_ClientId.label = options.ClientIdDisplayName;
|
|
m_ClientId.RegisterCallback<ChangeEvent<string>>(OnClientIdChanged);
|
|
|
|
m_ClientSecret = containerUI.Q<TextField>("id-provider-client-secret");
|
|
if (options.NeedClientSecret)
|
|
{
|
|
m_ClientSecret.label = options.ClientSecretDisplayName;
|
|
m_ClientSecret.RegisterCallback<ChangeEvent<string>>(OnClientSecretChanged);
|
|
}
|
|
else
|
|
{
|
|
m_ClientSecret.style.display = DisplayStyle.None;
|
|
}
|
|
|
|
m_SaveButton = containerUI.Q<Button>("id-provider-save");
|
|
m_SaveButton.SetEnabled(false);
|
|
m_SaveButton.clicked += OnSaveButtonClicked;
|
|
|
|
m_CancelButton = containerUI.Q<Button>("id-provider-cancel");
|
|
m_CancelButton.SetEnabled(false);
|
|
m_CancelButton.clicked += OnCancelButtonClicked;
|
|
|
|
m_DeleteButton = containerUI.Q<Button>("id-provider-delete");
|
|
m_DeleteButton.clicked += OnDeleteButtonClicked;
|
|
m_DeleteButton.SetEnabled(!savedValue.New);
|
|
|
|
ResetCurrentValue();
|
|
Add(containerUI);
|
|
}
|
|
else
|
|
{
|
|
throw new Exception("Asset not found: " + k_ElementUxml);
|
|
}
|
|
}
|
|
|
|
void RefreshButtons()
|
|
{
|
|
bool hasChanges = Changed;
|
|
m_SaveButton.SetEnabled(hasChanges && IsValid);
|
|
m_CancelButton.SetEnabled(hasChanges || SavedValue.New);
|
|
if (SavedValue.New)
|
|
{
|
|
m_DeleteButton.SetEnabled(false);
|
|
m_DeleteButton.style.display = DisplayStyle.None;
|
|
}
|
|
else
|
|
{
|
|
m_DeleteButton.SetEnabled(true);
|
|
m_DeleteButton.style.display = DisplayStyle.Flex;
|
|
}
|
|
}
|
|
|
|
void OnEnabledChanged(ChangeEvent<bool> e)
|
|
{
|
|
CurrentValue.Disabled = !e.newValue;
|
|
RefreshButtons();
|
|
}
|
|
|
|
void OnClientIdChanged(ChangeEvent<string> e)
|
|
{
|
|
CurrentValue.ClientId = e.newValue;
|
|
RefreshButtons();
|
|
}
|
|
|
|
void OnClientSecretChanged(ChangeEvent<string> e)
|
|
{
|
|
CurrentValue.ClientSecret = e.newValue;
|
|
RefreshButtons();
|
|
}
|
|
|
|
void OnSaveButtonClicked()
|
|
{
|
|
int option = DisplayDialogComplex("Save your changes", "Do you want to save the ID provider changes?", "Save", "Cancel", "");
|
|
switch (option)
|
|
{
|
|
case 0:
|
|
Waiting?.Invoke(this, true);
|
|
|
|
if (SavedValue.New)
|
|
{
|
|
var asyncOp = m_AdminClient.CreateIdProvider(m_IdDomainId, new CreateIdProviderRequest(CurrentValue));
|
|
asyncOp.Completed += OnSaveCompleted;
|
|
}
|
|
else
|
|
{
|
|
var body = new UpdateIdProviderRequest(CurrentValue);
|
|
var asyncOp = m_AdminClient.UpdateIdProvider(m_IdDomainId, CurrentValue.Type, body);
|
|
asyncOp.Completed += OnSaveCompleted;
|
|
}
|
|
break;
|
|
|
|
case 1:
|
|
break;
|
|
|
|
default:
|
|
Debug.LogError("Unrecognized option.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OnSaveCompleted(IAsyncOperation<IdProviderResponse> asyncOp)
|
|
{
|
|
if (asyncOp.Exception != null)
|
|
{
|
|
Error?.Invoke(this, AuthenticationSettingsHelper.ExtractException(asyncOp.Exception));
|
|
Waiting?.Invoke(this, false);
|
|
return;
|
|
}
|
|
|
|
SavedValue = asyncOp.Result;
|
|
|
|
// Check enable/disable status
|
|
if (SavedValue.Disabled != CurrentValue.Disabled)
|
|
{
|
|
SavedValue.ClientSecret = CurrentValue.ClientSecret;
|
|
asyncOp = CurrentValue.Disabled ? m_AdminClient.DisableIdProvider(m_IdDomainId, CurrentValue.Type) : m_AdminClient.EnableIdProvider(m_IdDomainId, CurrentValue.Type);
|
|
asyncOp.Completed += OnEnableDisableCompleted;
|
|
return;
|
|
}
|
|
|
|
// Enable/disable is not changed
|
|
ResetCurrentValue();
|
|
RefreshButtons();
|
|
Waiting?.Invoke(this, false);
|
|
}
|
|
|
|
void OnEnableDisableCompleted(IAsyncOperation<IdProviderResponse> asyncOp)
|
|
{
|
|
// Handle enable/disable exception
|
|
if (asyncOp.Exception != null)
|
|
{
|
|
Error?.Invoke(this, AuthenticationSettingsHelper.ExtractException(asyncOp.Exception));
|
|
Waiting?.Invoke(this, false);
|
|
return;
|
|
}
|
|
|
|
// Only reset current value when no exception
|
|
SavedValue = asyncOp.Result;
|
|
ResetCurrentValue();
|
|
RefreshButtons();
|
|
Waiting?.Invoke(this, false);
|
|
}
|
|
|
|
void OnCancelButtonClicked()
|
|
{
|
|
if (SavedValue.New)
|
|
{
|
|
// It's a new ID provider and it hasn't been saved to the server yet.
|
|
// Simply trigger delete event to notify parent to remove the element from the list.
|
|
Deleted?.Invoke(this);
|
|
return;
|
|
}
|
|
ResetCurrentValue();
|
|
}
|
|
|
|
void ResetCurrentValue()
|
|
{
|
|
CurrentValue = SavedValue.Clone();
|
|
m_Enabled.value = !CurrentValue.Disabled;
|
|
m_ClientId.value = CurrentValue.ClientId ?? "";
|
|
m_ClientSecret.value = CurrentValue.ClientSecret ?? "";
|
|
|
|
RefreshButtons();
|
|
}
|
|
|
|
void OnDeleteButtonClicked()
|
|
{
|
|
int option = DisplayDialogComplex("Delete Request", "Do you want to delete the ID Provider?", "Delete", "Cancel", "");
|
|
switch (option)
|
|
{
|
|
// Delete
|
|
case 0:
|
|
Waiting?.Invoke(this, true);
|
|
var asyncOp = m_AdminClient.DeleteIdProvider(m_IdDomainId, CurrentValue.Type);
|
|
asyncOp.Completed += OnDeleteCompleted;
|
|
break;
|
|
|
|
// Cancel
|
|
case 1:
|
|
break;
|
|
|
|
default:
|
|
Debug.LogError("Unrecognized option.");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void OnDeleteCompleted(IAsyncOperation<IdProviderResponse> asyncOp)
|
|
{
|
|
if (asyncOp.Exception != null)
|
|
{
|
|
Error?.Invoke(this, AuthenticationSettingsHelper.ExtractException(asyncOp.Exception));
|
|
Waiting?.Invoke(this, false);
|
|
return;
|
|
}
|
|
|
|
// Simply trigger delete event to notify parent to remove the element from the list.
|
|
Deleted?.Invoke(this);
|
|
ResetCurrentValue();
|
|
Waiting?.Invoke(this, false);
|
|
}
|
|
|
|
int DisplayDialogComplex(string title, string message, string ok, string cancel, string alt)
|
|
{
|
|
if (Application.isBatchMode || m_SkipConfirmation)
|
|
return 0;
|
|
|
|
return EditorUtility.DisplayDialogComplex(title, message, ok, cancel, alt);
|
|
}
|
|
}
|
|
}
|