File: SudoEnvironmentDirectoryOverride.cs
Web Access
Project: ..\..\..\src\Cli\dotnet\dotnet.csproj (dotnet)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable disable
 
using System.CommandLine;
using Microsoft.DotNet.Cli.Commands.Workload;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
using NuGet.Common;
using NuGet.Configuration;
 
namespace Microsoft.DotNet.Cli;
 
/// <summary>
///  https://github.com/dotnet/sdk/issues/20195
/// </summary>
public static class SudoEnvironmentDirectoryOverride
{
    /// <summary>
    /// Not for security use. Detect if command is running under sudo
    /// via if SUDO_UID being set.
    /// </summary>
    public static bool IsRunningUnderSudo()
    {
        if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("SUDO_UID")))
        {
            return true;
        }
 
        return false;
    }
 
    public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult)
    {
        if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult))
        {
            string sudoHome = PathUtilities.CreateTempSubdirectory();
            var homeBeforeOverride = Environment.GetEnvironmentVariable(CliFolderPathCalculator.DotnetHomeVariableName);
            Environment.SetEnvironmentVariable(CliFolderPathCalculator.DotnetHomeVariableName, sudoHome);
 
            if (homeBeforeOverride is not null)
            {
                CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride);
            }
        }
    }
 
    /// <summary>
    /// To make NuGet honor the user's NuGet config file.
    /// Copying instead of using the file directly to avoid existing file being set higher permission
    /// Try to delete the existing NuGet config file in "/tmp/dotnet_sudo_home/"
    /// to avoid different user's NuGet config getting mixed.
    /// </summary>
    private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverride)
    {
        // https://github.com/NuGet/NuGet.Client/blob/dev/src/NuGet.Core/NuGet.Common/PathUtil/NuGetEnvironment.cs#L139
        // home is cache in NuGet we cannot directly use the call
        var userSettingsDir = Path.Combine(homeBeforeOverride, ".nuget", "NuGet");
 
        string userNuGetConfig = Settings.OrderedSettingsFileNames
            .Select(fileName => Path.Combine(userSettingsDir, fileName))
            .FirstOrDefault(f => File.Exists(f));
 
        var overriddenSettingsDir = NuGetEnvironment.GetFolderPath(NuGetFolderPath.UserSettingsDirectory);
        var overriddenNugetConfig = Path.Combine(overriddenSettingsDir, Settings.DefaultSettingsFileName);
 
        if (File.Exists(overriddenNugetConfig))
        {
            try
            {
                FileAccessRetrier.RetryOnIOException(
                    () => File.Delete(overriddenNugetConfig));
            }
            catch
            {
                // best effort to remove
            }
        }
 
        if (userNuGetConfig != default)
        {
            try
            {
                FileAccessRetrier.RetryOnIOException(
                    () => File.Copy(userNuGetConfig, overriddenNugetConfig, overwrite: true));
            }
            catch
            {
                // best effort to copy
            }
        }
    }
 
    private static bool IsRunningWorkloadCommand(ParseResult parseResult) =>
        parseResult.RootSubCommandResult() == (WorkloadCommandParser.GetCommand().Name);
}