Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public interface IRealmController

UniTask<bool> IsReachableAsync(URLDomain realm, CancellationToken ct);

UniTask<bool> IsUserAuthorisedToAccessWorldAsync(URLDomain realm, CancellationToken ct);

/// <summary>
/// Dispose everything on application quit
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using CommunicationData.URLHelpers;
using Cysharp.Threading.Tasks;
using DCL.Audio;
using DCL.Chat.History;
using DCL.DebugUtilities;
using DCL.Diagnostics;
using DCL.FeatureFlags;
Expand Down Expand Up @@ -287,27 +286,6 @@ public async UniTask LoadStartingRealmAsync(DynamicWorldContainer dynamicWorldCo
if (startingRealm.HasValue == false)
throw new InvalidOperationException("Starting realm is not set");

if (realmLaunchSettings.initialRealm is InitialRealm.World)
{
bool isAuthorized = await dynamicWorldContainer.RealmController
.IsUserAuthorisedToAccessWorldAsync(startingRealm.Value, ct);

if (!isAuthorized)
{
ReportHub.LogWarning(ReportCategory.REALM,
$"[Bootstrap] Startup world '{realmLaunchSettings.TargetWorld}' is not authorized for auto-entry, falling back to Genesis.");

dynamicWorldContainer.ChatHistory.AddMessage(
ChatChannel.NEARBY_CHANNEL_ID,
ChatChannel.ChatChannelType.NEARBY,
ChatMessage.NewFromSystem($"Could not auto-enter '{realmLaunchSettings.TargetWorld}' due to world permissions. You were sent to Genesis Plaza."));

await dynamicWorldContainer.RealmController
.SetRealmAsync(URLDomain.FromString(realmUrls.GenesisRealm()), ct);
return;
}
}

await dynamicWorldContainer.RealmController.SetRealmAsync(startingRealm.Value, ct);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,6 @@ static IMultiPool MultiPoolFactory() =>

var realmContainer = RealmContainer.Create(
staticContainer,
identityCache,
dynamicWorldParams.StaticLoadPositions,
debugBuilder,
loadingScreenTimeout,
Expand All @@ -390,8 +389,7 @@ static IMultiPool MultiPoolFactory() =>
bootstrapContainer.DecentralandUrlsSource,
appArgs,
teleportController,
bootstrapContainer.Environment,
worldPermissionsService);
bootstrapContainer.Environment);

var terrainContainer = TerrainContainer.Create(staticContainer, realmContainer, dynamicWorldParams.EnableLandscape, localSceneDevelopment);

Expand Down Expand Up @@ -514,7 +512,9 @@ static IMultiPool MultiPoolFactory() =>
roomHub,
localSceneDevelopment,
staticContainer.CharacterContainer,
moderationDataProvider);
moderationDataProvider,
worldPermissionsService,
chatHistory);

IRealmNavigator realmNavigator = realmNavigatorContainer.RealmNavigator;
HomePlaceEventBus homePlaceEventBus = new HomePlaceEventBus();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using DCL.ApplicationBlocklistGuard;
using DCL.Audio;
using DCL.Character.Plugin;
using DCL.Chat.History;
using DCL.Diagnostics;
using DCL.Multiplayer.Connections.RoomHubs;
using DCL.Multiplayer.HealthChecks;
using DCL.PrivateWorlds;
using DCL.Profiles.Self;
using DCL.RealmNavigation;
using DCL.RealmNavigation.LoadingOperation;
Expand Down Expand Up @@ -37,7 +39,9 @@ public static InitializationFlowContainer Create(
IRoomHub roomHub,
bool localSceneDevelopment,
CharacterContainer characterContainer,
ModerationDataProvider moderationDataProvider)
ModerationDataProvider moderationDataProvider,
IWorldPermissionsService worldPermissionsService,
IChatHistory chatHistory)
{
ILoadingStatus? loadingStatus = staticContainer.LoadingStatus;

Expand Down Expand Up @@ -100,7 +104,9 @@ public static InitializationFlowContainer Create(
characterContainer.CharacterObject,
characterContainer.Transform,
dynamicWorldParams.StartParcel,
localSceneDevelopment),
localSceneDevelopment,
worldPermissionsService,
chatHistory),
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using DCL.Ipfs;
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.Optimization.Pools;
using DCL.PluginSystem.Global;
using DCL.Utilities;
using DCL.Utilities.Extensions;
using DCL.Web3.Identities;
Expand Down Expand Up @@ -54,11 +53,9 @@ public class RealmController : IGlobalRealmController

private readonly List<ISceneFacade> allScenes = new (PoolConstants.SCENES_COUNT);
private readonly ServerAbout serverAbout = new ();
private readonly IWeb3IdentityCache web3IdentityCache;
private readonly IWebRequestController webRequestController;
private readonly IReadOnlyList<int2> staticLoadPositions;
private readonly RealmData realmData;
private readonly IWorldPermissionsService worldPermissionsService;
private readonly RetrieveSceneFromFixedRealm retrieveSceneFromFixedRealm;
private readonly RetrieveSceneFromVolatileWorld retrieveSceneFromVolatileWorld;
private readonly TeleportController teleportController;
Expand Down Expand Up @@ -92,7 +89,6 @@ public GlobalWorld GlobalWorld
}

public RealmController(
IWeb3IdentityCache web3IdentityCache,
IWebRequestController webRequestController,
TeleportController teleportController,
RetrieveSceneFromFixedRealm retrieveSceneFromFixedRealm,
Expand All @@ -107,14 +103,11 @@ public RealmController(
IAppArgs appArgs,
IDecentralandUrlsSource decentralandUrlsSource,
DecentralandEnvironment environment,
WorldManifestProvider worldManifestProvider,
IWorldPermissionsService worldPermissionsService)
WorldManifestProvider worldManifestProvider)
{
this.web3IdentityCache = web3IdentityCache;
this.webRequestController = webRequestController;
this.staticLoadPositions = staticLoadPositions;
this.realmData = realmData;
this.worldPermissionsService = worldPermissionsService;
this.teleportController = teleportController;
this.retrieveSceneFromFixedRealm = retrieveSceneFromFixedRealm;
this.retrieveSceneFromVolatileWorld = retrieveSceneFromVolatileWorld;
Expand Down Expand Up @@ -203,31 +196,6 @@ public async UniTask SetRealmAsync(URLDomain realm, CancellationToken ct)
public async UniTask<bool> IsReachableAsync(URLDomain realm, CancellationToken ct) =>
await webRequestController.IsHeadReachableAsync(ReportCategory.REALM, realm.Append(new URLPath("/about")), ct);

public async UniTask<bool> IsUserAuthorisedToAccessWorldAsync(URLDomain realm, CancellationToken ct)
{
if (!TryExtractWorldName(realm, out string worldName))
{
ReportHub.LogWarning(ReportCategory.REALM,
$"[RealmController] Failed to extract world name from realm '{realm}'.");
return false;
}

WorldAccessCheckContext context;
try
{
context = await worldPermissionsService.CheckWorldAccessAsync(worldName, ct);
}
catch (OperationCanceledException) { throw; }
catch (Exception e)
{
ReportHub.LogWarning(ReportCategory.REALM,
$"[RealmController] Failed to verify world access for '{worldName}' via world permissions: {e.Message}");
return false;
}

return context.Result == WorldAccessCheckResult.Allowed;
}

private static bool TryExtractWorldName(URLDomain realm, out string worldName)
{
worldName = string.Empty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ public class RealmContainer

public static RealmContainer Create(
StaticContainer staticContainer,
IWeb3IdentityCache identityCache,
IReadOnlyList<int2> staticLoadPositions,
IDebugContainerBuilder debugContainerBuilder,
LoadingScreenTimeout loadingScreenTimeout,
Expand All @@ -41,16 +40,14 @@ public static RealmContainer Create(
IDecentralandUrlsSource urlsSource,
IAppArgs appArgs,
TeleportController teleportController,
DecentralandEnvironment dclEnvironment,
IWorldPermissionsService worldPermissionsService)
DecentralandEnvironment dclEnvironment)
{
var retrieveSceneFromFixedRealm = new RetrieveSceneFromFixedRealm();
var retrieveSceneFromVolatileWorld = new RetrieveSceneFromVolatileWorld(staticContainer.RealmData, urlsSource);

var realmNavigatorDebugView = new RealmNavigatorDebugView(debugContainerBuilder);

var realmController = new RealmController(
identityCache,
staticContainer.WebRequestsContainer.WebRequestController,
teleportController,
retrieveSceneFromFixedRealm,
Expand All @@ -66,8 +63,7 @@ public static RealmContainer Create(
appArgs,
urlsSource,
dclEnvironment,
staticContainer.WorldManifestProvider,
worldPermissionsService
staticContainer.WorldManifestProvider
);

BuildDebugWidget(teleportController, debugContainerBuilder, loadingScreen, loadingScreenTimeout);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"GUID:c80c82a8f4e04453b85fbab973d6774a",
"GUID:54d33bbd50a28174e8ba0110106203c2",
"GUID:875a5d5129614170bd769ed012c2eb3d",
"GUID:8614faad1014549c88b57654b0fb41bd"
"GUID:8614faad1014549c88b57654b0fb41bd",
"GUID:f8127c6ac263abf468221dcbccbde182"
],
"includePlatforms": [],
"excludePlatforms": [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,27 @@
using DCL.Audio;
using DCL.AuthenticationScreenFlow;
using DCL.Character;
using DCL.Chat.History;
using DCL.Diagnostics;
using DCL.Multiplayer.Connections.DecentralandUrls;
using DCL.Multiplayer.Connections.RoomHubs;
using DCL.Prefs;
using DCL.PrivateWorlds;
using DCL.RealmNavigation;
using DCL.RealmNavigation.LoadingOperation;
using DCL.SceneLoadingScreens.LoadingScreen;
using DCL.UI.ErrorPopup;
using DCL.Utilities;
using DCL.Utility.Types;
using DCL.Web3.Identities;
using ECS;
using ECS.SceneLifeCycle.Realm;
using Global.AppArgs;
using MVC;
using PortableExperiences.Controller;
using UnityEngine;
using Utility;
using ChatMessage = DCL.Chat.History.ChatMessage;

namespace DCL.UserInAppInitializationFlow
{
Expand Down Expand Up @@ -50,6 +54,8 @@ public class RealUserInAppInitializationFlow : IUserInAppInitializationFlow
private readonly ExposedTransform characterExposedTransform;
private readonly StartParcel startParcel;
private readonly bool isLocalSceneDevelopment;
private readonly IWorldPermissionsService worldPermissionsService;
private readonly IChatHistory chatHistory;

public RealUserInAppInitializationFlow(
ILoadingStatus loadingStatus,
Expand All @@ -69,7 +75,9 @@ public RealUserInAppInitializationFlow(
ICharacterObject characterObject,
ExposedTransform characterExposedTransform,
StartParcel startParcel,
bool isLocalSceneDevelopment)
bool isLocalSceneDevelopment,
IWorldPermissionsService worldPermissionsService,
IChatHistory chatHistory)
{
this.initOps = initOps;
this.reloginOps = reloginOps;
Expand All @@ -80,6 +88,8 @@ public RealUserInAppInitializationFlow(
this.startParcel = startParcel;
this.isLocalSceneDevelopment = isLocalSceneDevelopment;
this.characterExposedTransform = characterExposedTransform;
this.worldPermissionsService = worldPermissionsService;
this.chatHistory = chatHistory;

this.loadingStatus = loadingStatus;
this.decentralandUrlsSource = decentralandUrlsSource;
Expand Down Expand Up @@ -161,6 +171,11 @@ await UniTask.WhenAll(
.ShowWhileExecuteTaskAsync(
async (parentLoadReport, ct) =>
{
// After authentication completes, verify the user can actually access the current realm if it's a world.
// The realm was set during bootstrap before the user had a chance to switch accounts, so the identity
// that's now authenticated may differ from the one assumed at startup.
await VerifyWorldAccessAndFallbackIfNeededAsync(ct);

//Set initial position and start async livekit connection
characterExposedTransform.Position.Value
= characterObject.Controller.transform.position
Expand Down Expand Up @@ -221,6 +236,82 @@ await UniTask.WhenAll(
while (result.Success == false && parameters.ShowAuthentication);
}

private async UniTask VerifyWorldAccessAndFallbackIfNeededAsync(CancellationToken ct)
{
if (isLocalSceneDevelopment) return;
if (!realmController.RealmData.IsWorld()) return;
if (realmController.CurrentDomain == null) return;

if (!TryExtractWorldName(realmController.CurrentDomain.Value, out string worldName))
{
ReportHub.LogWarning(ReportCategory.REALM,
$"[RealmController] Failed to extract world name from realm '{realmController.CurrentDomain.Value.ToString()}'.");
await GenesisFallbackAsync();
return;
}

WorldAccessCheckContext context;

try
{
context = await worldPermissionsService.CheckWorldAccessAsync(worldName, ct);
}
catch (OperationCanceledException) { throw; }
catch (Exception e)
{
ReportHub.LogWarning(ReportCategory.REALM,
$"[StartUp] Failed to verify world access for '{worldName}' via world permissions: {e.Message}");
await GenesisFallbackAsync();
return;
}

switch (context.Result)
{
case WorldAccessCheckResult.Allowed:
return;
case WorldAccessCheckResult.CheckFailed:
case WorldAccessCheckResult.AccessDenied:
case WorldAccessCheckResult.PasswordRequired:
ReportHub.LogWarning(ReportCategory.REALM,
$"[StartUp] World '{worldName}' is not authorized for auto-entry, falling back to Genesis.");
await GenesisFallbackAsync();
return;
default: throw new ArgumentOutOfRangeException();
}

async UniTask GenesisFallbackAsync()
{
chatHistory.AddMessage(
ChatChannel.NEARBY_CHANNEL_ID,
ChatChannel.ChatChannelType.NEARBY,
ChatMessage.NewFromSystem($"Could not enter '{worldName}' due to world permissions. You were sent to Genesis Plaza."));

await realmController.SetRealmAsync(
URLDomain.FromString(decentralandUrlsSource.Url(DecentralandUrl.Genesis)), ct);
}
}

private static bool TryExtractWorldName(URLDomain realm, out string worldName)
{
worldName = string.Empty;

if (!Uri.TryCreate(realm.Value, UriKind.Absolute, out Uri? uri))
return false;

string path = uri.AbsolutePath.Trim('/');

if (string.IsNullOrEmpty(path))
return false;

string[] segments = path.Split('/', StringSplitOptions.RemoveEmptyEntries);

if (segments.Length == 0)
return false;

worldName = segments[^1];
return !string.IsNullOrEmpty(worldName);
}

// TODO should be an operation
private async UniTask DoLogoutOperationsAsync()
{
Expand Down
Loading