2025-01-07 09:40:39 +01:00
// Copyright (C) 2025, The Duplicati Team
2024-02-28 15:45:30 +01:00
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System.Collections.Generic ;
2018-03-22 15:20:17 -06:00
using System.Linq ;
2024-10-01 09:30:02 +02:00
using System.Threading ;
using System.Threading.Tasks ;
2018-03-22 15:20:17 -06:00
using Duplicati.Library.Backend.MicrosoftGraph ;
2018-03-20 14:18:49 -06:00
using Duplicati.Library.Interface ;
2024-10-01 09:30:02 +02:00
using Duplicati.Library.Utility ;
2018-03-20 14:18:49 -06:00
namespace Duplicati.Library.Backend
{
public class MicrosoftGroup : MicrosoftGraphBackend
{
2018-03-22 15:20:17 -06:00
private const string GROUP_EMAIL_OPTION = "group-email" ;
2018-03-20 14:18:49 -06:00
private const string GROUP_ID_OPTION = "group-id" ;
2018-09-30 10:56:59 -07:00
private const string PROTOCOL_KEY = "msgroup" ;
2018-03-20 14:18:49 -06:00
private readonly string drivePath ;
public MicrosoftGroup () { } // Constructor needed for dynamic loading to find it
public MicrosoftGroup ( string url , Dictionary < string , string > options )
2018-09-30 10:59:26 -07:00
: base ( url , MicrosoftGroup . PROTOCOL_KEY , options )
2018-03-20 14:18:49 -06:00
{
2018-03-22 15:20:17 -06:00
string groupId = null ;
string groupEmail ;
if ( options . TryGetValue ( GROUP_EMAIL_OPTION , out groupEmail ))
2018-03-20 14:18:49 -06:00
{
2024-10-01 09:30:02 +02:00
// TODO: Should not make network requests in constructor
groupId = this . GetGroupIdFromEmailAsync ( groupEmail , CancellationToken . None ). Await ();
2018-03-20 14:18:49 -06:00
}
2018-03-22 15:20:17 -06:00
string groupIdOption ;
if ( options . TryGetValue ( GROUP_ID_OPTION , out groupIdOption ))
{
if (! string . IsNullOrEmpty ( groupId ) && ! string . Equals ( groupId , groupIdOption ))
{
throw new UserInformationException ( Strings . MicrosoftGroup . ConflictingGroupId ( groupIdOption , groupId ), "MicrosoftGroupConflictingGroupId" );
}
groupId = groupIdOption ;
}
2024-10-01 09:30:02 +02:00
2018-03-22 15:20:17 -06:00
if ( string . IsNullOrEmpty ( groupId ))
2018-03-20 14:18:49 -06:00
{
2018-03-23 15:03:04 -06:00
throw new UserInformationException ( Strings . MicrosoftGroup . MissingGroupIdAndEmailAddress , "MicrosoftGroupMissingGroupIdAndEmailAddress" );
2018-03-20 14:18:49 -06:00
}
2018-03-22 15:20:17 -06:00
this . drivePath = string . Format ( "/groups/{0}/drive" , groupId );
2018-03-20 14:18:49 -06:00
}
public override string ProtocolKey
{
2018-09-30 10:56:59 -07:00
get { return MicrosoftGroup . PROTOCOL_KEY ; }
2018-03-20 14:18:49 -06:00
}
public override string DisplayName
{
get { return Strings . MicrosoftGroup . DisplayName ; }
}
protected override string DrivePath
{
get { return this . drivePath ; }
}
protected override DescriptionTemplateDelegate DescriptionTemplate
{
get
{
return Strings . MicrosoftGroup . Description ;
}
}
protected override IList < ICommandLineArgument > AdditionalSupportedCommands
{
get
{
2024-10-01 09:30:02 +02:00
return
[
2018-03-20 14:18:49 -06:00
new CommandLineArgument(GROUP_ID_OPTION, CommandLineArgument.ArgumentType.String, Strings.MicrosoftGroup.GroupIdShort, Strings.MicrosoftGroup.GroupIdLong),
2018-03-22 16:14:24 -06:00
new CommandLineArgument(GROUP_EMAIL_OPTION, CommandLineArgument.ArgumentType.String, Strings.MicrosoftGroup.GroupEmailShort, Strings.MicrosoftGroup.GroupEmailLong),
2024-10-01 09:30:02 +02:00
] ;
2018-03-20 14:18:49 -06:00
}
}
2018-03-22 15:20:17 -06:00
2024-10-01 09:30:02 +02:00
private async Task < string > GetGroupIdFromEmailAsync ( string email , CancellationToken cancelToken )
2018-03-22 15:20:17 -06:00
{
// We can get all groups that have the given email as one of their addresses with:
// https://graph.microsoft.com/v1.0/groups?$filter=mail eq '{email}' or proxyAddresses/any(x:x eq 'smtp:{email}')
string request = string . Format ( "{0}/groups?$filter=mail eq '{1}' or proxyAddresses/any(x:x eq 'smtp:{1}')" , this . ApiVersion , email );
2024-10-01 09:30:02 +02:00
var groups = await this . GetAsync < GraphCollection < Group >>( request , cancelToken ). ConfigureAwait ( false );
2018-03-22 15:20:17 -06:00
if ( groups . Value . Length == 0 )
{
throw new UserInformationException ( Strings . MicrosoftGroup . NoGroupsWithEmail ( email ), "MicrosoftGroupNoGroupsWithEmail" );
}
else if ( groups . Value . Length > 1 )
{
throw new UserInformationException ( Strings . MicrosoftGroup . MultipleGroupsWithEmail ( email ), "MicrosoftGroupMultipleGroupsWithEmail" );
}
else
{
return groups . Value . Single (). Id ;
}
}
2018-03-20 14:18:49 -06:00
}
}