diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs index 3d4edb37..75faa9e5 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Security/UserActions.cs @@ -120,20 +120,21 @@ namespace Microsoft.SqlTools.ServiceLayer.Security User existingUser = dataContainer.Server.Databases[parentDb.Name].Users[parameters.Name]; userType = UserActions.GetCurrentUserTypeForExistingUser(existingUser); DatabaseUserType databaseUserType = UserActions.GetDatabaseUserTypeForUserType(userType); + + // if contained user determine if SQL or AAD auth type + ServerAuthenticationType authenticationType = + (databaseUserType == DatabaseUserType.Contained && userType == ExhaustiveUserTypes.ExternalUser) + ? ServerAuthenticationType.AzureActiveDirectory : ServerAuthenticationType.Sql; + userInfo = new UserInfo() { Type = databaseUserType, + AuthenticationType = authenticationType, Name = parameters.Name, LoginName = existingUser.Login, DefaultSchema = existingUser.DefaultSchema, }; - // update the authentication type for contained users - if (databaseUserType == DatabaseUserType.Contained) - { - userInfo.AuthenticationType = ServerAuthenticationType.Sql; - } - // Default language is only applicable for users inside a contained database. if (LanguageUtils.IsDefaultLanguageSupported(dataContainer.Server) && parentDb.ContainmentType != ContainmentType.None) @@ -222,7 +223,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security }, SupportContainedUser = supportsContainedUser, SupportWindowsAuthentication = false, - SupportAADAuthentication = false, + SupportAADAuthentication = currentUserPrototype.AADAuthSupported, SupportSQLAuthentication = true, Languages = languageOptionsList.ToArray(), Schemas = currentUserPrototype.SchemaNames.ToArray(), @@ -490,7 +491,14 @@ namespace Microsoft.SqlTools.ServiceLayer.Security userType = ExhaustiveUserTypes.WindowsUser; break; case DatabaseUserType.Contained: - userType = ExhaustiveUserTypes.SqlUserWithPassword; + if (user.AuthenticationType == ServerAuthenticationType.AzureActiveDirectory) + { + userType = ExhaustiveUserTypes.ExternalUser; + } + else + { + userType = ExhaustiveUserTypes.SqlUserWithPassword; + } break; case DatabaseUserType.NoConnectAccess: userType = ExhaustiveUserTypes.SqlUserWithoutLogin; @@ -516,6 +524,9 @@ namespace Microsoft.SqlTools.ServiceLayer.Security case ExhaustiveUserTypes.SqlUserWithoutLogin: databaseUserType = DatabaseUserType.NoConnectAccess; break; + case ExhaustiveUserTypes.ExternalUser: + databaseUserType = DatabaseUserType.Contained; + break; } return databaseUserType; } @@ -548,6 +559,8 @@ namespace Microsoft.SqlTools.ServiceLayer.Security return ExhaustiveUserTypes.CertificateMappedUser; case UserType.AsymmetricKey: return ExhaustiveUserTypes.AsymmetricKeyMappedUser; + case UserType.External: + return ExhaustiveUserTypes.ExternalUser; default: return ExhaustiveUserTypes.Unknown; } diff --git a/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs b/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs index a6a7d8d4..6d8737ac 100644 --- a/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs +++ b/src/Microsoft.SqlTools.ServiceLayer/Security/UserData.cs @@ -146,6 +146,12 @@ namespace Microsoft.SqlTools.ServiceLayer.Security case DatabaseUserType.NoConnectAccess: userType = UserType.NoLogin; break; + case DatabaseUserType.Contained: + if (userInfo.AuthenticationType == ServerAuthenticationType.AzureActiveDirectory) + { + userType = UserType.External; + } + break; // all the other user types are using SqlLogin } return userType; @@ -361,6 +367,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security private List roleNames; private bool exists = false; private Database parent; + private CDataContainer context; public bool IsRoleMembershipChangesApplied { get; set; } //default is false public bool IsSchemaOwnershipChangesApplied { get; set; } //default is false @@ -494,6 +501,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security this.currentState = current; this.originalState = original; this.exists = !context.IsNewObject; + this.context = context; Database? parent = context.Server.GetSmoObject(new Urn(context.ParentUrn)) as Database ?? throw new ArgumentException("Context ParentUrn is invalid"); this.parent = parent; @@ -691,6 +699,22 @@ namespace Microsoft.SqlTools.ServiceLayer.Security return result; } + + public bool AADAuthSupported + { + get + { + return context?.Server?.ServerType == DatabaseEngineType.SqlAzureDatabase; + } + } + + public bool WindowsAuthSupported + { + get + { + return context?.Server?.ServerType != DatabaseEngineType.SqlAzureDatabase; + } + } } internal class UserPrototypeWithDefaultSchema : UserPrototype, @@ -1029,6 +1053,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security break; case ExhaustiveUserTypes.SqlUserWithoutLogin: + case ExhaustiveUserTypes.ExternalUser: currentPrototype ??= new UserPrototypeWithDefaultSchema(context, currentData, originalData); break; @@ -1060,6 +1085,7 @@ namespace Microsoft.SqlTools.ServiceLayer.Security WindowsUser, LoginMappedUser, CertificateMappedUser, - AsymmetricKeyMappedUser + AsymmetricKeyMappedUser, + ExternalUser, }; }