private IEnumerable <MethodInfo> GetAllActions(IEnumerable <ControllerActionRuleException> exceptions = null) { return(Assembly.GetAssembly(typeof(AppController)) .GetTypes() // Get all types that are controllers. .Where(typeof(Controller).IsAssignableFrom) // Get all public methods of those types. .SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) // Filter out compiler generated methods. .Where(m => !m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), inherit: true).Any()) .OrderBy(DisplayMethodName) // Filter out exceptions. .Where(m => { var possibleException = new ControllerActionRuleException(m); var isNotException = !exceptions?.Any(a => a.Equals(possibleException)) ?? true; if (!isNotException) { _output.WriteLine($"{DisplayMethodName(m)} - excluded due to exception"); } return isNotException; })); }
public void AllActionsHaveAntiForgeryTokenIfNotGet() { // Arrange // If an action only supports these verbs, it doesn't need the anti-forgery token attribute. var verbExceptions = new HttpVerbs[] { HttpVerbs.Get, HttpVerbs.Head }; // These actions cannot have the AntiForgery token attribute. // For example: API methods intended to be called by clients. var exceptions = new ControllerActionRuleException[] { new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackagePut)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreateSymbolPackagePutAsync)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackagePost)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackageVerificationKeyAsync)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.DeletePackage)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.PublishPackage)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.DeprecatePackage)), new ControllerActionRuleException(typeof(AuthenticationController), nameof(AuthenticationController.AuthenticateAndLinkExternal)), new ControllerActionRuleException(typeof(AuthenticationController), nameof(AuthenticationController.ChallengeAuthentication)) }; // Act var assembly = Assembly.GetAssembly(typeof(AppController)); var actions = GetAllActions(exceptions) // Filter out methods that only support verbs that are exceptions. .Where(m => { var attributes = m.GetCustomAttributes() .Where(a => typeof(ActionMethodSelectorAttribute).IsAssignableFrom(a.GetType())); return(!(attributes .All(a => a.GetType() == typeof(HttpGetAttribute) || a.GetType() == typeof(HttpHeadAttribute) || (a.GetType() == typeof(AcceptVerbsAttribute) && (a as AcceptVerbsAttribute).Verbs.All(v => verbExceptions.Any(ve => v.Equals(ve.ToString(), StringComparison.InvariantCultureIgnoreCase))))))); }); // Assert var actionsMissingAntiForgeryToken = actions .Where(m => !(m.GetCustomAttributes().Any(a => a.GetType() == typeof(ValidateAntiForgeryTokenAttribute)))); Assert.Empty(actionsMissingAntiForgeryToken); }
private IEnumerable <MethodInfo> GetAllActions(IEnumerable <ControllerActionRuleException> exceptions = null) { return(Assembly.GetAssembly(typeof(AppController)).GetTypes() // Get all types that are controllers. .Where(t => typeof(Controller).IsAssignableFrom(t)) // Get all public methods of those types. .SelectMany(t => t.GetMethods(BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.Public)) // Filter out compiler generated methods. .Where(m => !m.GetCustomAttributes(typeof(CompilerGeneratedAttribute), true).Any()) // Filter out exceptions. .Where(m => { var possibleException = new ControllerActionRuleException(m); return !exceptions?.Any(a => a.Equals(possibleException)) ?? true; })); }
public void AllActionsHaveUIAuthorizeAttribute() { // Arrange // These actions are allowed to continue to support a discontinued login. var expectedActionsSupportingDiscontinuedLogins = new ControllerActionRuleException[] { new ControllerActionRuleException(typeof(AccountsController <,>), nameof(UsersController.Confirm)), new ControllerActionRuleException(typeof(AccountsController <,>), nameof(UsersController.ConfirmationRequired)), new ControllerActionRuleException(typeof(AccountsController <,>), nameof(UsersController.ConfirmationRequiredPost)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.Thanks)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.TransformToOrganization)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.ConfirmTransformToOrganizationRedirect)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.ConfirmTransformToOrganization)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.RejectTransformToOrganizationRedirect)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.RejectTransformToOrganization)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.CancelTransformToOrganizationRedirect)), new ControllerActionRuleException(typeof(UsersController), nameof(UsersController.CancelTransformToOrganization)), }; // Act var assembly = Assembly.GetAssembly(typeof(AppController)); var actions = GetAllActions(); // Assert // No actions should have the base authorize attribute! var actionsWithBaseAuthorizeAttribute = actions .Where(m => m.GetCustomAttributes().Any(a => a.GetType() == typeof(AuthorizeAttribute))); Assert.Empty(actionsWithBaseAuthorizeAttribute); // Only certain actions should support discontinued password login var actionsSupportingDiscontinuedLogins = actions .Where(m => m.GetCustomAttributes().Any(a => a is UIAuthorizeAttribute && ((UIAuthorizeAttribute)a).AllowDiscontinuedLogins)) .Select(m => new ControllerActionRuleException(m)) .Distinct(); Assert.Empty(actionsSupportingDiscontinuedLogins.Except(expectedActionsSupportingDiscontinuedLogins)); Assert.Empty(expectedActionsSupportingDiscontinuedLogins.Except(actionsSupportingDiscontinuedLogins)); }
public void AllActionsHaveAntiForgeryTokenIfNotGet() { // Arrange // If an action only supports these verbs, it doesn't need the anti-forgery token attribute. var verbExceptions = new HttpVerbs[] { HttpVerbs.Get, HttpVerbs.Head }; // These actions cannot have the AntiForgery token attribute. // For example: API methods intended to be called by clients. var exceptions = new ControllerActionRuleException[] { new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackagePut)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreateSymbolPackagePutAsync)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackagePost)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.CreatePackageVerificationKeyAsync)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.DeletePackage)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.PublishPackage)), new ControllerActionRuleException(typeof(ApiController), nameof(ApiController.DeprecatePackage)), }; // Act var assembly = Assembly.GetAssembly(typeof(AppController)); var actions = GetAllActions(exceptions) // Filter out methods that only support verbs that are exceptions. .Where(m => { var attributes = m.GetCustomAttributes() .Where(a => typeof(ActionMethodSelectorAttribute) .IsAssignableFrom(a.GetType())); // Non-actions cannot be requested via HTTP. if (attributes.Any(a => a.GetType() == typeof(NonActionAttribute))) { _output.WriteLine($"{DisplayMethodName(m)} - non-action"); return(false); } var isGetOrHead = attributes .All(a => a.GetType() == typeof(HttpGetAttribute) || a.GetType() == typeof(HttpHeadAttribute) || (a.GetType() == typeof(AcceptVerbsAttribute) && (a as AcceptVerbsAttribute).Verbs.All(v => verbExceptions.Any(ve => v.Equals(ve.ToString(), StringComparison.InvariantCultureIgnoreCase))))); if (isGetOrHead) { _output.WriteLine($"{DisplayMethodName(m)} - GET or HEAD"); } return(!isGetOrHead); }) .Select(m => { _output.WriteLine($"{DisplayMethodName(m)} - to be verified"); return(m); }); // Assert var actionsMissingAntiForgeryToken = actions .Where(m => !(m.GetCustomAttributes().Any(a => a.GetType() == typeof(ValidateAntiForgeryTokenAttribute)))); Assert.Empty(actionsMissingAntiForgeryToken); }