diff --git a/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationCodeProcessor.cs b/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationCodeProcessor.cs index 5e2584ac..5c53f4be 100644 --- a/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationCodeProcessor.cs +++ b/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationCodeProcessor.cs @@ -14,7 +14,8 @@ namespace OpenRiaServices.Server.Authentication /// /// implementation that sets the base class of both the /// context and entity types generated by a provider implementing - /// . + /// . or + /// . /// internal sealed class AuthenticationCodeProcessor : CodeProcessor { @@ -263,19 +264,19 @@ internal static CodeCommentStatementCollection GetDocComments(string resourceCom } /// - /// Validates that the authentication service implements the interface + /// Validates that the authentication service implements or interface /// naturally for use in codegen. /// /// /// This check ensures no part of the interface was implemented explicitly. /// /// The domain service description for the type that implemented - /// the interface. + /// the or interface. /// - /// The generic version of implemented + /// The generic version of or implemented /// by the service type of the . /// - /// is thrown if the interface + /// is thrown if the or interface /// is not correctly implemented. /// private static void CheckIAuthentication(DomainServiceDescription authenticationServiceDescription, out Type genericIAuthenticationType) @@ -285,7 +286,7 @@ private static void CheckIAuthentication(DomainServiceDescription authentication bool implementsGetUser = false; bool implementsUpdateUser = false; - if (!typeof(IAuthentication<>).DefinitionIsAssignableFrom(authenticationServiceDescription.DomainServiceType, out genericIAuthenticationType)) + if (!authenticationServiceDescription.TryGetAuthenticationServiceType(out genericIAuthenticationType)) { throw new InvalidOperationException(Resources.ApplicationServices_MustBeIAuth); } @@ -296,16 +297,16 @@ private static void CheckIAuthentication(DomainServiceDescription authentication { switch (doe.Name) { - case "Login": + case string name when name.StartsWith("Login", StringComparison.OrdinalIgnoreCase): implementsLogin = CheckIAuthenticationLogin(doe, userType); break; - case "Logout": + case string name when name.StartsWith("Logout", StringComparison.OrdinalIgnoreCase): implementsLogout = CheckIAuthenticationLogout(doe, userType); break; - case "GetUser": + case string name when name.StartsWith("GetUser", StringComparison.OrdinalIgnoreCase): implementsGetUser = CheckIAuthenticationGetUser(doe, userType); break; - case "UpdateUser": + case string name when name.StartsWith("UpdateUser", StringComparison.OrdinalIgnoreCase): implementsUpdateUser = CheckIAuthenticationUpdateUser(doe, userType); break; default: @@ -323,10 +324,10 @@ private static void CheckIAuthentication(DomainServiceDescription authentication } /// - /// Validates that the operation entry represents for use in codegen. + /// Validates that the operation entry represents or for use in codegen. /// /// The entry to validate - /// The user type. T in . + /// The user type. T in or . /// Whether the operation entry represents Login private static bool CheckIAuthenticationLogin(DomainOperationEntry doe, Type userType) { @@ -355,10 +356,10 @@ private static bool CheckIAuthenticationLogin(DomainOperationEntry doe, Type use } /// - /// Validates that the operation entry represents for use in codegen. + /// Validates that the operation entry represents or for use in codegen. /// /// The entry to validate - /// The user type. T in . + /// The user type. T in or . /// Whether the operation entry represents Logout private static bool CheckIAuthenticationLogout(DomainOperationEntry doe, Type userType) { @@ -383,10 +384,10 @@ private static bool CheckIAuthenticationLogout(DomainOperationEntry doe, Type us } /// - /// Validates that the operation entry represents for use in codegen. + /// Validates that the operation entry represents or for use in codegen. /// /// The entry to validate - /// The user type. T in . + /// The user type. T in or . /// Whether the operation entry represents GetUser private static bool CheckIAuthenticationGetUser(DomainOperationEntry doe, Type userType) { @@ -411,10 +412,10 @@ private static bool CheckIAuthenticationGetUser(DomainOperationEntry doe, Type u } /// - /// Validates that the operation entry represents for use in codegen. + /// Validates that the operation entry represents or for use in codegen. /// /// The entry to validate - /// The user type. T in . + /// The user type. T in or . /// Whether the operation entry represents UpdateUser private static bool CheckIAuthenticationUpdateUser(DomainOperationEntry doe, Type userType) { diff --git a/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationServiceAttribute.cs b/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationServiceAttribute.cs index 5583df97..81f8bac1 100644 --- a/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationServiceAttribute.cs +++ b/src/OpenRiaServices.Server/Framework/Authentication/AuthenticationServiceAttribute.cs @@ -7,7 +7,7 @@ namespace OpenRiaServices.Server.Authentication /// /// /// This attribute is used to associate the with - /// an implementation of the interface. + /// an implementation of the and interface. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false, Inherited = true)] public sealed class AuthenticationServiceAttribute : DomainIdentifierAttribute diff --git a/src/OpenRiaServices.Server/Framework/Authentication/IAsyncAuthentication.cs b/src/OpenRiaServices.Server/Framework/Authentication/IAsyncAuthentication.cs new file mode 100644 index 00000000..5276e0ea --- /dev/null +++ b/src/OpenRiaServices.Server/Framework/Authentication/IAsyncAuthentication.cs @@ -0,0 +1,74 @@ +using System.Threading.Tasks; + +namespace OpenRiaServices.Server.Authentication +{ + /// + /// An interface for a that encapsulates the authentication domain. A + /// domain service implementing this interface will be used to populate the user on the client. + /// + /// + /// OpenRiaServices.Client.Authentication.WebAuthenticationService + /// will work with the DomainContext generated for any domain service implementing this + /// interface. + /// + /// is designed as an update method, and will be invoked via + /// SubmitChanges on the client. This has a couple implications. First, + /// invoking via AuthenticationService.SaveUser will submit all + /// changes that have occurred in the DomainContext and may invoke other update methods. + /// Second, invoking other update methods on the DomainContext from the client will submit + /// all changes and may invoke . + /// + /// + /// The type of the user entity + [AuthenticationService] + public interface IAuthenticationAsync where T : IUser + { + /// + /// Authenticates and returns the user with the specified name and password. + /// + /// + /// This method will return a single user if the + /// authentication was successful. If the user could not be authenticated, null + /// will be returned. + /// + /// The userName associated with the user to authenticate + /// The password associated with the user to authenticate + /// Whether the authentication should persist between sessions + /// Optional implementation-specific data + /// A single user or null if authentication failed + [Query(IsComposable = false, HasSideEffects = true)] + Task LoginAsync(string userName, string password, bool isPersistent, string customData); + + /// + /// Logs an authenticated user out. + /// + /// + /// This method will return a single, anonymous user. + /// + /// A single, default user. + [Query(IsComposable = false, HasSideEffects = true)] + Task LogoutAsync(); + + /// + /// Gets the principal and profile for the current user. + /// + /// + /// This method will return a single user. If the user is not + /// authenticated, an anonymous user will be returned. + /// + /// An enumerable with a single user. + [Query(IsComposable = false)] + Task GetUserAsync(); + + /// + /// Updates the profile for the authenticated user. + /// + /// The updated user + /// is thrown if the authenticated + /// user does not have the correct permissions to update the profile. + /// + [Update] + [RequiresAuthentication] + Task UpdateUserAsync(T user); + } +} diff --git a/src/OpenRiaServices.Server/Framework/Authentication/IUser.cs b/src/OpenRiaServices.Server/Framework/Authentication/IUser.cs index a616cb6a..2be9f75b 100644 --- a/src/OpenRiaServices.Server/Framework/Authentication/IUser.cs +++ b/src/OpenRiaServices.Server/Framework/Authentication/IUser.cs @@ -6,7 +6,7 @@ namespace OpenRiaServices.Server.Authentication /// Interface for user entities that has properties for passing principal values to the client. /// /// - /// This class is designed for use with the interface. + /// This class is designed for use with the or interface. /// It provides properties to support serialization of principal values to an entity class /// generated from a type implementing this interface. /// diff --git a/src/OpenRiaServices.Server/Framework/Data/DomainServiceDescription.cs b/src/OpenRiaServices.Server/Framework/Data/DomainServiceDescription.cs index 0fd09965..6fb6b02f 100644 --- a/src/OpenRiaServices.Server/Framework/Data/DomainServiceDescription.cs +++ b/src/OpenRiaServices.Server/Framework/Data/DomainServiceDescription.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Reflection; using System.Runtime.Serialization; +using OpenRiaServices.Server.Authentication; namespace OpenRiaServices.Server { @@ -2081,5 +2082,13 @@ private HashSet GetOrCreateRootEntityTypes() } return this._rootEntityTypes; } + + public bool IsAuthenticationService() => TryGetAuthenticationServiceType(out var _); + + public bool TryGetAuthenticationServiceType(out Type genericType) + { + return typeof(IAuthentication<>).DefinitionIsAssignableFrom(DomainServiceType, out genericType) + || typeof(IAuthenticationAsync<>).DefinitionIsAssignableFrom(DomainServiceType, out genericType); + } } } diff --git a/src/OpenRiaServices.Server/Test/Authentication/AuthenticationCodeProcessorTest.cs b/src/OpenRiaServices.Server/Test/Authentication/AuthenticationCodeProcessorTest.cs index 7ecfaf2d..981592f6 100644 --- a/src/OpenRiaServices.Server/Test/Authentication/AuthenticationCodeProcessorTest.cs +++ b/src/OpenRiaServices.Server/Test/Authentication/AuthenticationCodeProcessorTest.cs @@ -6,6 +6,7 @@ using Microsoft.CSharp; using Microsoft.VisualStudio.TestTools.UnitTesting; using OpenRiaServices.Server.Authentication; +using System.Threading.Tasks; namespace OpenRiaServices.Server.Test.Authentication { @@ -214,26 +215,26 @@ private static void Generate() null /* unused in negative tests */); } - public class AcpDomainService : DomainService, IAuthentication where T : IUser + public class AcpDomainService : DomainService, IAuthenticationAsync where T : IUser { #region IAuthentication Members - public T Login(string userName, string password, bool isPersistent, string customData) + public Task LoginAsync(string userName, string password, bool isPersistent, string customData) { throw new NotImplementedException(); } - public T Logout() + public Task LogoutAsync() { throw new NotImplementedException(); } - public T GetUser() + public Task GetUserAsync() { throw new NotImplementedException(); } - public void UpdateUser(T user) + public Task UpdateUserAsync(T user) { throw new NotImplementedException(); } diff --git a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpDomainContextGenerator.cs b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpDomainContextGenerator.cs index 25ac9473..f8032319 100644 --- a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpDomainContextGenerator.cs +++ b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpDomainContextGenerator.cs @@ -66,7 +66,7 @@ private void GenerateUsings() protected virtual void GenerateClassDeclaration() { string baseType = "OpenRiaServices.Client.DomainContext"; - if(typeof(IAuthentication<>).DefinitionIsAssignableFrom(this.DomainServiceDescription.DomainServiceType)) + if(DomainServiceDescription.IsAuthenticationService()) { baseType = @"global::OpenRiaServices.Client.Authentication.AuthenticationDomainContextBase"; } diff --git a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.cs b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.cs index 14f05769..46f6db7d 100644 --- a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.cs +++ b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.cs @@ -94,12 +94,10 @@ protected virtual void GenerateProperties() DomainServiceDescription defaultAuthDescription = this.GetDefaultAuthDescription(); - if(defaultAuthDescription != null) + if(defaultAuthDescription != null + && defaultAuthDescription.TryGetAuthenticationServiceType(out Type genericType) + && (genericType.GetGenericArguments().Count() == 1)) { - Type genericType = null; - typeof(IAuthentication<>).DefinitionIsAssignableFrom(defaultAuthDescription.DomainServiceType, out genericType); - if ((genericType != null) && (genericType.GetGenericArguments().Count() == 1)) - { string typeName = CodeGenUtilities.GetTypeName(genericType.GetGenericArguments()[0]); this.Write("public new "); @@ -112,8 +110,6 @@ protected virtual void GenerateProperties() this.Write(")base.User; }\r\n}\r\n"); - - } } } diff --git a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.partial.cs b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.partial.cs index 798d4f48..eb5fe396 100644 --- a/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.partial.cs +++ b/src/OpenRiaServices.Tools.TextTemplate/Framework/CSharpGenerators/CSharpWebContextGenerator.partial.cs @@ -54,7 +54,7 @@ private DomainServiceDescription GetDefaultAuthDescription() { DomainServiceDescription defaultAuthDescription = null; IEnumerable authDescriptions = - this.DomainServiceDescriptions.Where(d => typeof(IAuthentication<>).DefinitionIsAssignableFrom(d.DomainServiceType)); + this.DomainServiceDescriptions.Where(d => d.IsAuthenticationService()); if (authDescriptions.Count() > 1) { this.ClientCodeGenerator.CodeGenerationHost.LogMessage( diff --git a/src/OpenRiaServices.Tools/Framework/WebContextGenerator.cs b/src/OpenRiaServices.Tools/Framework/WebContextGenerator.cs index 764bb9b9..db452fb7 100644 --- a/src/OpenRiaServices.Tools/Framework/WebContextGenerator.cs +++ b/src/OpenRiaServices.Tools/Framework/WebContextGenerator.cs @@ -54,7 +54,8 @@ public override void Generate() // Find the AuthenticationServices and if there's just one, use it as the default. IEnumerable authDescriptions = - this.ClientProxyGenerator.DomainServiceDescriptions.Where(d => typeof(IAuthentication<>).DefinitionIsAssignableFrom(d.DomainServiceType)); + this.ClientProxyGenerator.DomainServiceDescriptions + .Where(d => d.IsAuthenticationService()); DomainServiceDescription defaultAuthDescription = null; if (authDescriptions.Count() > 1) { @@ -148,29 +149,26 @@ public override void Generate() // } // --> // ---------------------------------------------------------------- - if (defaultAuthDescription != null) + if (defaultAuthDescription != null + && defaultAuthDescription.TryGetAuthenticationServiceType(out Type genericType) + && (genericType.GetGenericArguments().Length == 1)) { - Type genericType = null; - typeof(IAuthentication<>).DefinitionIsAssignableFrom(defaultAuthDescription.DomainServiceType, out genericType); - if ((genericType != null) && (genericType.GetGenericArguments().Length == 1)) - { - CodeMemberProperty userProperty = new CodeMemberProperty(); - - userProperty.Attributes = MemberAttributes.Public | MemberAttributes.New | MemberAttributes.Final; - userProperty.Type = CodeGenUtilities.GetTypeReference( - genericType.GetGenericArguments()[0], this.ClientProxyGenerator, proxyClass); - userProperty.Name = "User"; - userProperty.HasGet = true; - userProperty.GetStatements.Add( - new CodeMethodReturnStatement( - new CodeCastExpression(userProperty.Type, - new CodePropertyReferenceExpression( - new CodeBaseReferenceExpression(), - "User")))); - userProperty.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentUser, this.ClientProxyGenerator.IsCSharp)); - - proxyClass.Members.Add(userProperty); - } + CodeMemberProperty userProperty = new CodeMemberProperty(); + + userProperty.Attributes = MemberAttributes.Public | MemberAttributes.New | MemberAttributes.Final; + userProperty.Type = CodeGenUtilities.GetTypeReference( + genericType.GetGenericArguments()[0], this.ClientProxyGenerator, proxyClass); + userProperty.Name = "User"; + userProperty.HasGet = true; + userProperty.GetStatements.Add( + new CodeMethodReturnStatement( + new CodeCastExpression(userProperty.Type, + new CodePropertyReferenceExpression( + new CodeBaseReferenceExpression(), + "User")))); + userProperty.Comments.AddRange(CodeGenUtilities.GetDocComments(Resource.WebContext_CommentUser, this.ClientProxyGenerator.IsCSharp)); + + proxyClass.Members.Add(userProperty); } } #endregion