123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590 |
- using UnityEngine;
- using UnityEditor;
- using VRC.Core;
- using System.Text.RegularExpressions;
-
- public partial class VRCSdkControlPanel : EditorWindow
- {
- static bool isInitialized = false;
- static string clientInstallPath;
- static bool signingIn = false;
- static string error = null;
-
- public static bool FutureProofPublishEnabled { get { return UnityEditor.EditorPrefs.GetBool("futureProofPublish", DefaultFutureProofPublishEnabled); } }
- public static bool DefaultFutureProofPublishEnabled { get { return !SDKClientUtilities.IsInternalSDK(); } }
-
- static string storedUsername
- {
- get
- {
- if (EditorPrefs.HasKey("sdk#username"))
- return EditorPrefs.GetString("sdk#username");
- return null;
- }
- set
- {
- EditorPrefs.SetString("sdk#username", value);
- if (string.IsNullOrEmpty(value))
- EditorPrefs.DeleteKey("sdk#username");
- }
- }
-
- static string storedPassword
- {
- get
- {
- if (EditorPrefs.HasKey("sdk#password"))
- return EditorPrefs.GetString("sdk#password");
- return null;
- }
- set
- {
- EditorPrefs.SetString("sdk#password", value);
- if (string.IsNullOrEmpty(value))
- EditorPrefs.DeleteKey("sdk#password");
- }
- }
-
- static string _username = null;
- static string _password = null;
-
- static string username
- {
- get
- {
- if (!string.IsNullOrEmpty(_username))
- return _username;
- else
- _username = storedUsername;
- return _username;
- }
- set
- {
- _username = value;
- }
- }
-
- static string password
- {
- get
- {
- if (!string.IsNullOrEmpty(_password))
- return _password;
- else
- _password = storedPassword;
- return _password;
- }
- set
- {
- _password = value;
- }
- }
-
- static ApiServerEnvironment serverEnvironment
- {
- get
- {
- ApiServerEnvironment env = ApiServerEnvironment.Release;
- try
- {
- env = (ApiServerEnvironment)System.Enum.Parse(typeof(ApiServerEnvironment), UnityEditor.EditorPrefs.GetString("VRC_ApiServerEnvironment", env.ToString()));
- }
- catch (System.Exception e)
- {
- Debug.LogError("Invalid server environment name - " + e.ToString());
- }
-
- return env;
- }
- set
- {
- UnityEditor.EditorPrefs.SetString("VRC_ApiServerEnvironment", value.ToString());
-
- API.SetApiUrlFromEnvironment(value);
- }
- }
-
- private void OnEnableAccount()
- {
- entered2faCodeIsInvalid = false;
- warningIconGraphic = Resources.Load("2FAIcons/SDK_Warning_Triangle_icon") as Texture2D;
- }
-
- public static void RefreshApiUrlSetting()
- {
- // this forces the static api url variable to be reset from the server environment set in editor prefs.
- // needed because the static variable states get cleared when entering / exiting play mode
- ApiServerEnvironment env = serverEnvironment;
- serverEnvironment = env;
- }
-
- public static void InitAccount()
- {
- if (isInitialized)
- return;
-
- if (!APIUser.IsLoggedInWithCredentials && ApiCredentials.Load())
- APIUser.FetchCurrentUser((c) => AnalyticsSDK.LoggedInUserChanged(c.Model as APIUser), null);
-
- clientInstallPath = SDKClientUtilities.GetSavedVRCInstallPath();
- if (string.IsNullOrEmpty(clientInstallPath))
- clientInstallPath = SDKClientUtilities.LoadRegistryVRCInstallPath();
-
- signingIn = false;
- isInitialized = true;
-
- ClearContent();
- }
-
- public static bool OnShowStatus()
- {
- API.SetOnlineMode(true);
-
- SignIn(false);
-
- EditorGUILayout.BeginVertical();
-
- if (APIUser.IsLoggedInWithCredentials)
- {
- OnCreatorStatusGUI();
- }
-
- EditorGUILayout.EndVertical();
-
- return APIUser.IsLoggedInWithCredentials;
- }
-
- static bool OnAccountGUI()
- {
- const int ACCOUNT_LOGIN_BORDER_SPACING = 20;
-
- EditorGUILayout.Separator();
- EditorGUILayout.Separator();
- EditorGUILayout.Separator();
- EditorGUILayout.Separator();
-
- GUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
- GUILayout.Space(ACCOUNT_LOGIN_BORDER_SPACING);
- GUILayout.BeginVertical("Account", "window", GUILayout.Height(150), GUILayout.Width(340));
-
- if (signingIn)
- {
- EditorGUILayout.LabelField("Signing in as " + username + ".");
- }
- else if (APIUser.IsLoggedInWithCredentials)
- {
- if (Status != "Connected")
- EditorGUILayout.LabelField(Status);
-
- OnCreatorStatusGUI();
-
- GUILayout.BeginHorizontal();
- GUILayout.Label("");
-
- if (GUILayout.Button("Logout"))
- {
- storedUsername = username = null;
- storedPassword = password = null;
-
- VRC.Tools.ClearCookies();
- APIUser.Logout();
- ClearContent();
- }
- GUILayout.EndHorizontal();
- }
- else
- {
- InitAccount();
-
- ApiServerEnvironment newEnv = ApiServerEnvironment.Release;
- if (VRCSettings.Get().DisplayAdvancedSettings)
- newEnv = (ApiServerEnvironment)EditorGUILayout.EnumPopup("Use API", serverEnvironment);
- if (serverEnvironment != newEnv)
- serverEnvironment = newEnv;
-
- username = EditorGUILayout.TextField("Username", username);
- password = EditorGUILayout.PasswordField("Password", password);
-
- if (GUILayout.Button("Sign In"))
- SignIn(true);
- if (GUILayout.Button("Sign up"))
- Application.OpenURL("http://vrchat.com/register");
- }
-
- if (showTwoFactorAuthenticationEntry)
- {
- OnTwoFactorAuthenticationGUI();
- }
-
- GUILayout.EndVertical();
- GUILayout.Space(ACCOUNT_LOGIN_BORDER_SPACING);
- GUILayout.FlexibleSpace();
- GUILayout.EndHorizontal();
-
- return !signingIn;
- }
-
- static void OnCreatorStatusGUI()
- {
- EditorGUILayout.LabelField("Logged in as:", APIUser.CurrentUser.displayName);
-
- if (SDKClientUtilities.IsInternalSDK())
- EditorGUILayout.LabelField("Developer Status: ", APIUser.CurrentUser.developerType.ToString());
-
- EditorGUILayout.BeginHorizontal();
-
- EditorGUILayout.BeginVertical();
- EditorGUILayout.LabelField("World Creator Status: ", APIUser.CurrentUser.canPublishWorlds ? "Allowed to publish worlds" : "Not yet allowed to publish worlds");
- EditorGUILayout.LabelField("Avatar Creator Status: ", APIUser.CurrentUser.canPublishAvatars ? "Allowed to publish avatars" : "Not yet allowed to publish avatars");
- EditorGUILayout.EndVertical();
-
- if (!APIUser.CurrentUser.canPublishAllContent)
- {
- if (GUILayout.Button("More Info..."))
- {
- VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
- }
- }
-
-
- EditorGUILayout.EndHorizontal();
- }
-
- void ShowAccount()
- {
- if (VRC.Core.RemoteConfig.IsInitialized())
- {
- if (VRC.Core.RemoteConfig.HasKey("sdkUnityVersion"))
- {
- string sdkUnityVersion = VRC.Core.RemoteConfig.GetString("sdkUnityVersion");
- if (string.IsNullOrEmpty(sdkUnityVersion))
- EditorGUILayout.LabelField("Could not fetch remote config.");
- else if (Application.unityVersion != sdkUnityVersion)
- {
- EditorGUILayout.LabelField("Unity Version", EditorStyles.boldLabel);
- EditorGUILayout.LabelField("Wrong Unity version. Please use " + sdkUnityVersion);
- }
- }
- }
- else
- {
- VRC.Core.API.SetOnlineMode(true, "vrchat");
- VRC.Core.RemoteConfig.Init();
- }
-
- OnAccountGUI();
- }
-
-
- private const string TWO_FACTOR_AUTHENTICATION_HELP_URL = "https://docs.vrchat.com/docs/setup-2fa";
-
- private const string ENTER_2FA_CODE_TITLE_STRING = "Enter a numeric code from your authenticator app (or one of your saved recovery codes).";
- private const string ENTER_2FA_CODE_LABEL_STRING = "Code:";
-
- private const string CHECKING_2FA_CODE_STRING = "Checking code...";
- private const string ENTER_2FA_CODE_INVALID_CODE_STRING = "Invalid Code";
-
- private const string ENTER_2FA_CODE_VERIFY_STRING = "Verify";
- private const string ENTER_2FA_CODE_CANCEL_STRING = "Cancel";
- private const string ENTER_2FA_CODE_HELP_STRING = "Help";
-
- private const int WARNING_ICON_SIZE = 60;
- private const int WARNING_FONT_HEIGHT = 18;
-
- static private Texture2D warningIconGraphic;
-
- static bool entered2faCodeIsInvalid;
- static bool authorizationCodeWasVerified;
-
- static private int previousAuthenticationCodeLength = 0;
- static bool checkingCode;
- static string authenticationCode = "";
-
- static System.Action onAuthenticationVerifiedAction;
-
- static bool _showTwoFactorAuthenticationEntry = false;
-
- static bool showTwoFactorAuthenticationEntry
- {
- get
- {
- return _showTwoFactorAuthenticationEntry;
- }
- set
- {
- _showTwoFactorAuthenticationEntry = value;
- authenticationCode = "";
- if (!_showTwoFactorAuthenticationEntry && !authorizationCodeWasVerified)
- Logout();
- }
- }
-
- static bool IsValidAuthenticationCodeFormat()
- {
- bool isValid2faAuthenticationCode = false;
-
- if (!string.IsNullOrEmpty(authenticationCode))
- {
- // check if the input is a valid 6-digit numberic code (ignoring spaces)
- Regex rx = new Regex(@"^(\s*\d\s*){6}$", RegexOptions.Compiled);
- MatchCollection matches6DigitCode = rx.Matches(authenticationCode);
- isValid2faAuthenticationCode = (matches6DigitCode.Count == 1);
- }
-
- return isValid2faAuthenticationCode;
- }
-
- static bool IsValidRecoveryCodeFormat()
- {
- bool isValid2faRecoveryCode = false;
-
- if (!string.IsNullOrEmpty(authenticationCode))
- {
- // check if the input is a valid 8-digit alpha-numberic code (format xxxx-xxxx) "-" is optional & ignore any spaces
- // OTP codes also exclude the letters i,l,o and the digit 1 to prevent any confusion
- Regex rx = new Regex(@"^(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}-?(\s*[a-hj-km-np-zA-HJ-KM-NP-Z02-9]\s*){4}$", RegexOptions.Compiled);
- MatchCollection matchesRecoveryCode = rx.Matches(authenticationCode);
- isValid2faRecoveryCode = (matchesRecoveryCode.Count == 1);
- }
-
- return isValid2faRecoveryCode;
- }
-
- static void OnTwoFactorAuthenticationGUI()
- {
- const int ENTER_2FA_CODE_BORDER_SIZE = 20;
- const int ENTER_2FA_CODE_BUTTON_WIDTH = 260;
- const int ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH = ENTER_2FA_CODE_BUTTON_WIDTH / 2;
- const int ENTER_2FA_CODE_ENTRY_REGION_WIDTH = 130;
- const int ENTER_2FA_CODE_MIN_WINDOW_WIDTH = ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH + ENTER_2FA_CODE_ENTRY_REGION_WIDTH + (ENTER_2FA_CODE_BORDER_SIZE * 3);
-
- bool isValidAuthenticationCode = IsValidAuthenticationCodeFormat();
- bool isValidRecoveryCode = IsValidRecoveryCodeFormat();
-
-
- // Invalid code text
- if (entered2faCodeIsInvalid)
- {
- GUIStyle s = new GUIStyle(EditorStyles.label);
- s.alignment = TextAnchor.UpperLeft;
- s.normal.textColor = Color.red;
- s.fontSize = WARNING_FONT_HEIGHT;
- s.padding = new RectOffset(0, 0, (WARNING_ICON_SIZE - s.fontSize) / 2, 0);
- s.fixedHeight = WARNING_ICON_SIZE;
-
- EditorGUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
-
- EditorGUILayout.BeginVertical();
- GUILayout.FlexibleSpace();
- EditorGUILayout.BeginHorizontal();
- var textDimensions = s.CalcSize(new GUIContent(ENTER_2FA_CODE_INVALID_CODE_STRING));
- GUILayout.Label(new GUIContent(warningIconGraphic), GUILayout.Width(WARNING_ICON_SIZE), GUILayout.Height(WARNING_ICON_SIZE));
- EditorGUILayout.LabelField(ENTER_2FA_CODE_INVALID_CODE_STRING, s, GUILayout.Width(textDimensions.x));
- EditorGUILayout.EndHorizontal();
- GUILayout.FlexibleSpace();
- EditorGUILayout.EndVertical();
-
- GUILayout.FlexibleSpace();
- EditorGUILayout.EndHorizontal();
- }
- else if (checkingCode)
- {
- // Display checking code message
- EditorGUILayout.BeginVertical();
- GUILayout.FlexibleSpace();
- EditorGUILayout.BeginHorizontal();
- GUIStyle s = new GUIStyle(EditorStyles.label);
- s.alignment = TextAnchor.MiddleCenter;
- s.fixedHeight = WARNING_ICON_SIZE;
- EditorGUILayout.LabelField(CHECKING_2FA_CODE_STRING, s, GUILayout.Height(WARNING_ICON_SIZE));
- EditorGUILayout.EndHorizontal();
- GUILayout.FlexibleSpace();
- EditorGUILayout.EndVertical();
- }
- else
- {
- EditorGUILayout.BeginHorizontal();
- GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
- GUILayout.FlexibleSpace();
- GUIStyle titleStyle = new GUIStyle(EditorStyles.label);
- titleStyle.alignment = TextAnchor.MiddleCenter;
- titleStyle.wordWrap = true;
- EditorGUILayout.LabelField(ENTER_2FA_CODE_TITLE_STRING, titleStyle, GUILayout.Width(ENTER_2FA_CODE_MIN_WINDOW_WIDTH - (2 * ENTER_2FA_CODE_BORDER_SIZE)), GUILayout.Height(WARNING_ICON_SIZE), GUILayout.ExpandHeight(true));
- GUILayout.FlexibleSpace();
- GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
- EditorGUILayout.EndHorizontal();
- }
-
- EditorGUILayout.BeginHorizontal();
- GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
- GUILayout.FlexibleSpace();
- Vector2 size = EditorStyles.boldLabel.CalcSize(new GUIContent(ENTER_2FA_CODE_LABEL_STRING));
- EditorGUILayout.LabelField(ENTER_2FA_CODE_LABEL_STRING, EditorStyles.boldLabel, GUILayout.MaxWidth(size.x));
- authenticationCode = EditorGUILayout.TextField(authenticationCode);
-
- // Verify 2FA code button
- if (GUILayout.Button(ENTER_2FA_CODE_VERIFY_STRING, GUILayout.Width(ENTER_2FA_CODE_VERIFY_BUTTON_WIDTH)))
- {
- checkingCode = true;
- APIUser.VerifyTwoFactorAuthCode(authenticationCode, isValidAuthenticationCode ? API2FA.TIME_BASED_ONE_TIME_PASSWORD_AUTHENTICATION : API2FA.ONE_TIME_PASSWORD_AUTHENTICATION, username, password,
- delegate
- {
- // valid 2FA code submitted
- entered2faCodeIsInvalid = false;
- authorizationCodeWasVerified = true;
- checkingCode = false;
- showTwoFactorAuthenticationEntry = false;
- if (null != onAuthenticationVerifiedAction)
- onAuthenticationVerifiedAction();
- },
- delegate
- {
- entered2faCodeIsInvalid = true;
- checkingCode = false;
- }
- );
- }
-
- GUILayout.FlexibleSpace();
- GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
- EditorGUILayout.EndHorizontal();
-
- GUILayout.FlexibleSpace();
-
- EditorGUILayout.BeginHorizontal();
- GUILayout.FlexibleSpace();
-
- // after user has entered an invalid code causing the invalid code message to be displayed,
- // edit the code will change it's length meaning it is invalid format, so we can clear the invalid code setting until they resubmit
- if (previousAuthenticationCodeLength != authenticationCode.Length)
- {
- previousAuthenticationCodeLength = authenticationCode.Length;
- entered2faCodeIsInvalid = false;
- }
-
- GUI.enabled = true;
- GUILayout.FlexibleSpace();
- GUILayout.Space(ENTER_2FA_CODE_BORDER_SIZE);
- EditorGUILayout.EndHorizontal();
-
- GUILayout.FlexibleSpace();
-
- // Two-Factor Authentication Help button
- EditorGUILayout.BeginHorizontal();
- if (GUILayout.Button(ENTER_2FA_CODE_HELP_STRING))
- {
- Application.OpenURL(TWO_FACTOR_AUTHENTICATION_HELP_URL);
- }
- EditorGUILayout.EndHorizontal();
-
- // Cancel button
- EditorGUILayout.BeginHorizontal();
- if (GUILayout.Button(ENTER_2FA_CODE_CANCEL_STRING))
- {
- showTwoFactorAuthenticationEntry = false;
- Logout();
- }
- EditorGUILayout.EndHorizontal();
- }
-
- private static string Status
- {
- get
- {
- if (!APIUser.IsLoggedInWithCredentials)
- return error == null ? "Please log in." : "Error in authenticating: " + error;
- if (signingIn)
- return "Logging in.";
- else
- {
- if( serverEnvironment == ApiServerEnvironment.Dev )
- return "Connected to " + serverEnvironment.ToString();
- return "Connected";
- }
- }
- }
-
- private static void OnAuthenticationCompleted()
- {
- AttemptLogin();
- }
-
- private static void AttemptLogin()
- {
- APIUser.Login(username, password,
- delegate (ApiModelContainer<APIUser> c)
- {
- APIUser user = c.Model as APIUser;
- if (c.Cookies.ContainsKey("auth"))
- ApiCredentials.Set(user.username, username, "vrchat", c.Cookies["auth"]);
- else
- ApiCredentials.SetHumanName(user.username);
- signingIn = false;
- error = null;
- storedUsername = username;
- storedPassword = password;
- AnalyticsSDK.LoggedInUserChanged(user);
-
- if (!APIUser.CurrentUser.canPublishAllContent)
- {
- if (UnityEditor.SessionState.GetString("HasShownContentPublishPermissionsDialogForUser", "") != user.id)
- {
- UnityEditor.SessionState.SetString("HasShownContentPublishPermissionsDialogForUser", user.id);
- VRCSdkControlPanel.ShowContentPublishPermissionsDialog();
- }
- }
- },
- delegate (ApiModelContainer<APIUser> c)
- {
- Logout();
- error = c.Error;
- VRC.Core.Logger.Log("Error logging in: " + error);
- },
- delegate (ApiModelContainer<API2FA> c)
- {
- API2FA model2FA = c.Model as API2FA;
- if (c.Cookies.ContainsKey("auth"))
- ApiCredentials.Set(username, username, "vrchat", c.Cookies["auth"]);
- showTwoFactorAuthenticationEntry = true;
- onAuthenticationVerifiedAction = OnAuthenticationCompleted;
- }
- );
- }
-
-
- private static object syncObject = new object();
- private static void SignIn(bool explicitAttempt)
- {
- lock (syncObject)
- {
- if (signingIn
- || APIUser.IsLoggedInWithCredentials
- || (!explicitAttempt && string.IsNullOrEmpty(storedUsername))
- || (!explicitAttempt && string.IsNullOrEmpty(storedPassword)))
- return;
-
- signingIn = true;
- }
-
- InitAccount();
-
- AttemptLogin();
- }
-
- public static void Logout()
- {
- signingIn = false;
- storedUsername = null;
- storedPassword = null;
- VRC.Tools.ClearCookies();
- APIUser.Logout();
- }
-
- private void AccountDestroy()
- {
- signingIn = false;
- isInitialized = false;
- }
- }
|