Ver código fonte

Updating und AD-Anbindung abgeschlossen!

Arne Diekmann 8 anos atrás
pai
commit
e6d2a5c0d1
42 arquivos alterados com 892 adições e 66 exclusões
  1. 1 1
      GreenTree.Nachtragsmanagement.Core/AppendixVersion.cs
  2. 49 0
      GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryContext.cs
  3. 35 0
      GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryGroup.cs
  4. 56 0
      GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryUser.cs
  5. 5 5
      GreenTree.Nachtragsmanagement.Core/Configuration/LdapServerElement.cs
  6. 7 0
      GreenTree.Nachtragsmanagement.Core/GreenTree.Nachtragsmanagement.Core.csproj
  7. 1 0
      GreenTree.Nachtragsmanagement.Core/packages.config
  8. 4 0
      GreenTree.Nachtragsmanagement.Data/GreenTree.Nachtragsmanagement.Data.csproj
  9. 1 0
      GreenTree.Nachtragsmanagement.Data/packages.config
  10. 7 0
      GreenTree.Nachtragsmanagement.Services/GreenTree.Nachtragsmanagement.Services.csproj
  11. 18 10
      GreenTree.Nachtragsmanagement.Services/Scheduling/NotificationScheduler.cs
  12. 11 0
      GreenTree.Nachtragsmanagement.Services/User/IUserService.cs
  13. 24 1
      GreenTree.Nachtragsmanagement.Services/User/UserService.cs
  14. 1 0
      GreenTree.Nachtragsmanagement.Services/packages.config
  15. 22 4
      GreenTree.Nachtragsmanagement.Web.Framework/ApplicationContext.cs
  16. 35 1
      GreenTree.Nachtragsmanagement.Web.Framework/Authorization/FunctionAuthorizeAttribute.cs
  17. 11 0
      GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj
  18. 1 0
      GreenTree.Nachtragsmanagement.Web.Framework/packages.config
  19. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/info-16.png
  20. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/info-24.png
  21. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/search-16-contrast.png
  22. BIN
      GreenTree.Nachtragsmanagement.Web/Content/Images/search-16.png
  23. 32 1
      GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs
  24. 54 0
      GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs
  25. 16 15
      GreenTree.Nachtragsmanagement.Web/Extensions/GridViewSettingsHelper.cs
  26. 12 1
      GreenTree.Nachtragsmanagement.Web/Extensions/ZipArchiveExtension.cs
  27. 3 0
      GreenTree.Nachtragsmanagement.Web/Global.asax.cs
  28. 16 0
      GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj
  29. 2 0
      GreenTree.Nachtragsmanagement.Web/Implementations/AppendixNotificationPlugin.cs
  30. 1 0
      GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs
  31. 14 0
      GreenTree.Nachtragsmanagement.Web/Models/Global/PasswordChangeDataModel.cs
  32. 106 0
      GreenTree.Nachtragsmanagement.Web/Update/UpdateProcess.cs
  33. 1 1
      GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs
  34. 3 0
      GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs
  35. 52 0
      GreenTree.Nachtragsmanagement.Web/Validation/Global/PasswordChangeModelValidator.cs
  36. 1 0
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml
  37. 76 19
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml
  38. 82 0
      GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserSearchPartial.cshtml
  39. 92 0
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_ChangePasswordPartial.cshtml
  40. 35 4
      GreenTree.Nachtragsmanagement.Web/Views/Shared/_Footer.cshtml
  41. 4 3
      GreenTree.Nachtragsmanagement.Web/Web.config
  42. 1 0
      GreenTree.Nachtragsmanagement.Web/packages.config

+ 1 - 1
GreenTree.Nachtragsmanagement.Core/AppendixVersion.cs

@@ -19,4 +19,4 @@ namespace GreenTree.Nachtragsmanagement.Core
             }
         }
     }
-}
+}

+ 49 - 0
GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryContext.cs

@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.DirectoryServices.Linq;
+using System.DirectoryServices.Linq.EntryObjects;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Authentication
+{
+    public class ActiveDirectoryContext : DirectoryContext
+    {
+        #region Properties
+
+        /// <summary>
+        /// Entry set for all users
+        /// </summary>
+        public IEntrySet<ActiveDirectoryUser> Users { get; set; }
+
+        /// <summary>
+        /// Entry set for all groups
+        /// </summary>
+        public IEntrySet<ActiveDirectoryGroup> Groups { get; set; }
+
+        #endregion
+
+        #region Ctor
+
+        /// <summary>
+        /// Initializes a new instance of ActiveDirectoryContext class
+        /// </summary>
+        public ActiveDirectoryContext()
+        {
+
+        }
+
+        /// <summary>
+        /// Initializes a new instance of ActiveDirectoryContext class
+        /// </summary>
+        public ActiveDirectoryContext(string connectionString, string userName, string password) 
+            : base(connectionString, userName, password)
+        {
+            Users = CreateEntrySet<ActiveDirectoryUser>();
+            Groups = CreateEntrySet<ActiveDirectoryGroup>();
+        }
+
+        #endregion
+    }
+}

+ 35 - 0
GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryGroup.cs

@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.DirectoryServices.Linq.Attributes;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Authentication
+{
+    [DirectoryType("group")]
+    public class ActiveDirectoryGroup
+    {
+        #region Properties
+
+        /// <summary>
+        /// Gibt den vollen Namen zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("name", false)]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gibt die Mitglieder zurück oder legt sie fest
+        /// </summary>
+        [DirectoryProperty("member", false)]
+        public string[] Members { get; set; }
+
+        /// <summary>
+        /// Gibt die Gruppen-Mitgliedschaften zurück oder legt sie fest
+        /// </summary>
+        [DirectoryProperty("memberOf", false)]
+        public string[] MemberOf { get; set; }
+
+        #endregion
+    }
+}

+ 56 - 0
GreenTree.Nachtragsmanagement.Core/Authentication/ActiveDirectoryUser.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.DirectoryServices.Linq.Attributes;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace GreenTree.Nachtragsmanagement.Core.Authentication
+{
+    /// <summary>
+    /// Die Klasse zur Abbildung eines Users in einem ActiveDirectory
+    /// </summary>
+    [DirectoryType("user")]
+    public class ActiveDirectoryUser
+    {
+        #region Properties        
+
+        /// <summary>
+        /// Gibt den vollen Namen zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("name", true)]
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Gibt den Vornamen zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("givenName", true)]
+        public string ForeName { get; set; }
+
+        /// <summary>
+        /// Gibt den Nachnamen zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("sn", true)]
+        public string LastName { get; set; }
+
+        /// <summary>
+        /// Gibt den Kontonamen zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("samAccountName", true)]
+        public string AccountName { get; set; }
+
+        /// <summary>
+        /// Gibt den Vorgesetzten (User-String) zurück oder legt ihn fest
+        /// </summary>
+        [DirectoryProperty("manager", true)]
+        public string ManagerString { get; set; }
+
+        /// <summary>
+        /// Gibt die Mail-Adresse zurück oder legt sie fest
+        /// </summary>
+        [DirectoryProperty("mail")]
+        public string Mail { get; set; }
+
+        #endregion
+    }
+}

+ 5 - 5
GreenTree.Nachtragsmanagement.Core/Configuration/LdapServerElement.cs

@@ -10,18 +10,18 @@ namespace GreenTree.Nachtragsmanagement.Core.Configuration
     public class LdapServerElement : ConfigurationElement
     {
         /// <summary>
-        /// LDAP server which should be connected to to authenticate the user
+        /// LDAP connection which should be used to to authenticate the user
         /// </summary>
-        [ConfigurationProperty("ldapServer", DefaultValue = "", IsRequired = false)]
-        public string SmtpServer
+        [ConfigurationProperty("ldapConnectionString", DefaultValue = "", IsRequired = false)]
+        public string LdapConnectionString
         {
             get
             {
-                return (string)this["ldapServer"];
+                return (string)this["ldapConnectionString"];
             }
             set
             {
-                this["ldapServer"] = value;
+                this["ldapConnectionString"] = value;
             }
         }
 

+ 7 - 0
GreenTree.Nachtragsmanagement.Core/GreenTree.Nachtragsmanagement.Core.csproj

@@ -47,6 +47,10 @@
     <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
     <Reference Include="System.Data.Entity" />
+    <Reference Include="System.DirectoryServices" />
+    <Reference Include="System.DirectoryServices.Linq, Version=1.2.2.1, Culture=neutral, PublicKeyToken=15767dc0c85f19f9, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.DirectoryServices.Linq.1.2.2.1\lib\net45\System.DirectoryServices.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System.Web" />
     <Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.Helpers.dll</HintPath>
@@ -76,6 +80,9 @@
   <ItemGroup>
     <Compile Include="AppDomainTypeFinder.cs" />
     <Compile Include="AppendixVersion.cs" />
+    <Compile Include="Authentication\ActiveDirectoryContext.cs" />
+    <Compile Include="Authentication\ActiveDirectoryGroup.cs" />
+    <Compile Include="Authentication\ActiveDirectoryUser.cs" />
     <Compile Include="Authentication\IUserHelper.cs" />
     <Compile Include="Authentication\UserContext.cs" />
     <Compile Include="Authentication\UserHelper.cs" />

+ 1 - 0
GreenTree.Nachtragsmanagement.Core/packages.config

@@ -7,4 +7,5 @@
   <package id="Microsoft.AspNet.WebPages" version="3.2.0" targetFramework="net452" />
   <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
   <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net452" />
+  <package id="System.DirectoryServices.Linq" version="1.2.2.1" targetFramework="net452" />
 </packages>

+ 4 - 0
GreenTree.Nachtragsmanagement.Data/GreenTree.Nachtragsmanagement.Data.csproj

@@ -39,6 +39,10 @@
     <Reference Include="System" />
     <Reference Include="System.ComponentModel.DataAnnotations" />
     <Reference Include="System.Core" />
+    <Reference Include="System.DirectoryServices" />
+    <Reference Include="System.DirectoryServices.Linq, Version=1.2.2.1, Culture=neutral, PublicKeyToken=15767dc0c85f19f9, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.DirectoryServices.Linq.1.2.2.1\lib\net45\System.DirectoryServices.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />
     <Reference Include="Microsoft.CSharp" />

+ 1 - 0
GreenTree.Nachtragsmanagement.Data/packages.config

@@ -1,4 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="EntityFramework" version="6.1.3" targetFramework="net452" />
+  <package id="System.DirectoryServices.Linq" version="1.2.2.1" targetFramework="net452" />
 </packages>

+ 7 - 0
GreenTree.Nachtragsmanagement.Services/GreenTree.Nachtragsmanagement.Services.csproj

@@ -33,6 +33,9 @@
     <Reference Include="Autofac, Version=4.0.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
       <HintPath>..\packages\Autofac.4.0.1\lib\net45\Autofac.dll</HintPath>
     </Reference>
+    <Reference Include="Autofac.Extras.Quartz">
+      <HintPath>..\..\Autofac.Extras.Quartz\Autofac.Extras.Quartz\bin\Debug\Autofac.Extras.Quartz.dll</HintPath>
+    </Reference>
     <Reference Include="Common.Logging, Version=3.3.1.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
       <HintPath>..\packages\Common.Logging.3.3.1\lib\net40\Common.Logging.dll</HintPath>
     </Reference>
@@ -49,6 +52,10 @@
     <Reference Include="System" />
     <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.DirectoryServices" />
+    <Reference Include="System.DirectoryServices.Linq, Version=1.2.2.1, Culture=neutral, PublicKeyToken=15767dc0c85f19f9, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.DirectoryServices.Linq.1.2.2.1\lib\net45\System.DirectoryServices.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System.Web" />
     <Reference Include="System.Xml.Linq" />
     <Reference Include="System.Data.DataSetExtensions" />

+ 18 - 10
GreenTree.Nachtragsmanagement.Services/Scheduling/NotificationScheduler.cs

@@ -1,4 +1,6 @@
-using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+using Autofac;
+using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
 using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Services.Misc;
 using Quartz;
@@ -17,6 +19,7 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
         private readonly INotificationService _notificationService;
         private readonly IMiscService _miscService;
         private readonly ILogger _logger;
+        private readonly IScheduler _scheduler;
 
         #endregion
 
@@ -28,11 +31,13 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
         public NotificationScheduler(
             INotificationService notificationService,
             IMiscService miscService,
-            ILogger logger)
+            ILogger logger,
+            IScheduler scheduler)
         {
             _notificationService = notificationService;
             _miscService = miscService;
             _logger = logger;
+            _scheduler = scheduler;
         }
 
         #endregion
@@ -42,10 +47,13 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
         /// </summary>
         public void Start()
         {
-            var scheduler = StdSchedulerFactory.GetDefaultScheduler();
+            //var scheduler = StdSchedulerFactory.GetDefaultScheduler();
 
-            scheduler.Clear();
-            scheduler.Start();
+            //scheduler.Clear();
+            //scheduler.Start();
+
+            _scheduler.Clear();
+            _scheduler.Start();
 
             var mailNotifications = _miscService.GetAllMailNotifications();
 
@@ -70,7 +78,7 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
                         .WithCronSchedule(mailNotification.CronExpression)
                         .Build();
 
-                    scheduler.ScheduleJob(job, trigger);
+                    _scheduler.ScheduleJob(job, trigger);
 
                     _logger.Information(
                         String.Format("Mailbenachrichtigung \"({0}) {1} - {2}\" erfolgreich eingeplant.", 
@@ -90,17 +98,17 @@ namespace GreenTree.Nachtragsmanagement.Services.Scheduling
         /// <param name="jobId">The job id.</param>
         public DateTime GetNextExecutionOfJob(string jobId)
         {
-            var scheduler = StdSchedulerFactory.GetDefaultScheduler();
+            //var scheduler = StdSchedulerFactory.GetDefaultScheduler();
 
             var jobKey = new JobKey(jobId);
             var nextFireTime = DateTime.MinValue;
 
-            var isJobExisting = scheduler.CheckExists(jobKey);
+            var isJobExisting = _scheduler.CheckExists(jobKey);
 
             if (isJobExisting)
             {
-                var detail = scheduler.GetJobDetail(jobKey);
-                var triggers = scheduler.GetTriggersOfJob(jobKey);
+                var detail = _scheduler.GetJobDetail(jobKey);
+                var triggers = _scheduler.GetTriggersOfJob(jobKey);
 
                 if (triggers.Count > 0)
                 {

+ 11 - 0
GreenTree.Nachtragsmanagement.Services/User/IUserService.cs

@@ -4,6 +4,7 @@ using System.Linq;
 using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
 
 namespace GreenTree.Nachtragsmanagement.Services.User
@@ -57,6 +58,16 @@ namespace GreenTree.Nachtragsmanagement.Services.User
 
         #endregion
 
+        #region ActiveDirectory
+
+        /// <summary>
+        /// Searches the ActiveDirectory for users matching the searchValue
+        /// </summary>
+        /// <param name="searchValue">The text to be searched for.</param>
+        IList<ActiveDirectoryUser> SearchUsers(string searchValue);
+
+        #endregion
+
         #region Role
 
         /// <summary>

+ 24 - 1
GreenTree.Nachtragsmanagement.Services/User/UserService.cs

@@ -1,10 +1,13 @@
 using System;
 using System.Collections.Generic;
+using System.DirectoryServices.Linq;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
 using GreenTree.Nachtragsmanagement.Core.Data;
 using GreenTree.Nachtragsmanagement.Core.Domain.User;
+using GreenTree.Nachtragsmanagement.Services.Configuration;
 
 namespace GreenTree.Nachtragsmanagement.Services.User
 {
@@ -18,6 +21,7 @@ namespace GreenTree.Nachtragsmanagement.Services.User
         private readonly IRepository<Core.Domain.User.User> _userRepository;
         private readonly IRepository<Role> _roleRepository;
         private readonly IRepository<Function> _functionRepository;
+        private readonly ActiveDirectoryContext _activeDirectoryContext;
 
         #endregion
 
@@ -29,11 +33,13 @@ namespace GreenTree.Nachtragsmanagement.Services.User
         public UserService(
             IRepository<Core.Domain.User.User> userRepository,
             IRepository<Role> roleRepository,
-            IRepository<Function> functionRepository)
+            IRepository<Function> functionRepository,
+            ActiveDirectoryContext activeDirectoryContext)
         {
             _userRepository = userRepository;
             _roleRepository = roleRepository;
             _functionRepository = functionRepository;
+            _activeDirectoryContext = activeDirectoryContext;
         }
 
         #endregion
@@ -106,6 +112,23 @@ namespace GreenTree.Nachtragsmanagement.Services.User
 
         #endregion
 
+        #region ActiveDirectory
+
+        /// <summary>
+        /// Searches the ActiveDirectory for users matching the searchValue
+        /// </summary>
+        /// <param name="searchValue">The text to be searched for.</param>
+        public IList<ActiveDirectoryUser> SearchUsers(string searchValue)
+        {
+            var users = _activeDirectoryContext.Users
+                .Where(u => u.AccountName.Contains(searchValue) || u.LastName.Contains(searchValue) || u.ForeName.Contains(searchValue))
+                .ToList();
+
+            return users;
+        }
+
+        #endregion
+
         #region Role
 
         /// <summary>

+ 1 - 0
GreenTree.Nachtragsmanagement.Services/packages.config

@@ -5,4 +5,5 @@
   <package id="Common.Logging.Core" version="3.3.1" targetFramework="net452" />
   <package id="Newtonsoft.Json" version="10.0.3" targetFramework="net452" />
   <package id="Quartz" version="2.6.0" targetFramework="net452" />
+  <package id="System.DirectoryServices.Linq" version="1.2.2.1" targetFramework="net452" />
 </packages>

+ 22 - 4
GreenTree.Nachtragsmanagement.Web.Framework/ApplicationContext.cs

@@ -29,6 +29,9 @@ using GreenTree.Nachtragsmanagement.Services.Appendix;
 using GreenTree.Nachtragsmanagement.Services.Scheduling;
 using GreenTree.Nachtragsmanagement.Services.Logging;
 using Autofac.Core;
+using Autofac.Extras.Quartz;
+using Quartz.Impl;
+using Quartz;
 
 namespace GreenTree.Nachtragsmanagement.Web.Framework
 {
@@ -98,8 +101,18 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
 
             var connection = WebConfigurationManager.ConnectionStrings["DefaultConnection"];
 
-            // Register object instances
-            builder.RegisterInstance(new AppendixObjectContext(connection.ConnectionString)).As<IDbContext>().SingleInstance();
+            var config = WebConfigurationManager.OpenWebConfiguration("~/");
+            var sectionGroup = config.GetSectionGroup("appendixSectionGroup");
+            var section = sectionGroup.Sections["appendixConfigSection"] as AppendixConfigurationSection;
+
+            // Register AppendixDataContext instance
+            builder.RegisterInstance(
+                new AppendixObjectContext(connection.ConnectionString)).As<IDbContext>().SingleInstance();
+
+            // Register ActiveDirectoryDataContext instance
+            builder.RegisterInstance(
+                new ActiveDirectoryContext(section.LdapServerElement.LdapConnectionString, section.LdapServerElement.AdministriveUser,
+                    section.LdapServerElement.Password));
 
             // Register generic data repositorys
             builder.RegisterGeneric(typeof(EfRepository<>)).As(typeof(IRepository<>));
@@ -109,7 +122,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
 
             // Register logger
             builder.RegisterType<DefaultLogger>().As<ILogger>();
-            builder.RegisterType<ServiceLogger>().As<IServiceLogger>();
+            builder.RegisterType<ServiceLogger>().As<IServiceLogger>().InstancePerLifetimeScope();
 
             // Register service types
             builder.RegisterType<DbRelationService>().As<IDbRelationService>();
@@ -125,7 +138,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
             builder.RegisterType<PluginFinder>().As<IPluginFinder>();
             builder.RegisterType<WebAppTypeFinder>().As<ITypeFinder>();
             builder.RegisterType<RoutePublisher>().As<IRoutePublisher>();
-            builder.RegisterType<NotificationScheduler>().As<INotificationScheduler>();
+
+            builder.RegisterModule(new QuartzAutofacFactoryModule());
 
             // Register controllers
             builder.RegisterControllers(Assembly.GetCallingAssembly());
@@ -165,8 +179,12 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework
                 notificationPluginBuilder.RegisterType(notificationPlugin.GetType())
                     .Named<INotificationPlugin>(notificationPlugin.SystemName)
                     .As<INotificationPlugin>();
+
+                notificationPluginBuilder.RegisterModule(new QuartzAutofacJobsModule(notificationPlugin.GetType().Assembly));
             }
 
+            notificationPluginBuilder.RegisterType<NotificationScheduler>().As<INotificationScheduler>();
+
             notificationPluginBuilder.Update(_appContainer);
 
             DependencyResolver.SetResolver(new AutofacDependencyResolver(_appContainer));

+ 35 - 1
GreenTree.Nachtragsmanagement.Web.Framework/Authorization/FunctionAuthorizeAttribute.cs

@@ -1,6 +1,7 @@
 using Autofac;
 using GreenTree.Nachtragsmanagement.Core;
 using GreenTree.Nachtragsmanagement.Core.Authentication;
+using GreenTree.Nachtragsmanagement.Core.Domain.User;
 using GreenTree.Nachtragsmanagement.Services.User;
 using System;
 using System.Collections.Generic;
@@ -49,7 +50,12 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework.Authorization
             var cookieUser = _userHelper.FromCookiesOrSession();
 
             if (cookieUser == null)
-                return false;
+            {
+                cookieUser = CheckWindowsAuthentication(httpContext);
+
+                if (cookieUser == null)
+                    return false;
+            }
 
             if (!_allowedFunctions.Any())
                 return true;
@@ -90,5 +96,33 @@ namespace GreenTree.Nachtragsmanagement.Web.Framework.Authorization
             else
                 filterContext.Result = new RedirectResult("~/global/notauthorized");
         }
+
+        #region Windows authentication
+
+        /// <summary>
+        /// Checks for Windows SSO authentication
+        /// </summary>
+        /// <param name="httpContext">Current HttpContext.</param>
+        private User CheckWindowsAuthentication(HttpContextBase httpContext)
+        {
+            if (httpContext.User == null || String.IsNullOrEmpty(httpContext.User.Identity.Name)) return null;
+
+            var username = httpContext.User.Identity.Name.Split('\\').Length > 1
+                ? httpContext.User.Identity.Name.Split('\\')[1]
+                : httpContext.User.Identity.Name;
+
+            var user = _userService.GetUserByCustomNumber(username);
+
+            if (user == null)
+                return null;
+
+            user.CurrentRole = user.Roles.First(r1 => r1.Level == user.Roles.Max(r2 => r2.Level));
+
+            _userHelper.ToCookiesAndSession(user, DateTime.Now.AddHours(8));
+
+            return user;
+        }
+
+        #endregion
     }
 }

+ 11 - 0
GreenTree.Nachtragsmanagement.Web.Framework/GreenTree.Nachtragsmanagement.Web.Framework.csproj

@@ -33,6 +33,9 @@
     <Reference Include="Autofac, Version=4.0.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
       <HintPath>..\packages\Autofac.4.0.1\lib\net45\Autofac.dll</HintPath>
     </Reference>
+    <Reference Include="Autofac.Extras.Quartz">
+      <HintPath>..\..\Autofac.Extras.Quartz\Autofac.Extras.Quartz\bin\Debug\Autofac.Extras.Quartz.dll</HintPath>
+    </Reference>
     <Reference Include="Autofac.Integration.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
       <HintPath>..\packages\Autofac.Mvc5.4.0.2\lib\net45\Autofac.Integration.Mvc.dll</HintPath>
     </Reference>
@@ -100,9 +103,17 @@
       <HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
       <Private>True</Private>
     </Reference>
+    <Reference Include="Quartz, Version=2.6.0.0, Culture=neutral, PublicKeyToken=f6b8c98a402cc8a4, processorArchitecture=MSIL">
+      <SpecificVersion>False</SpecificVersion>
+      <HintPath>..\..\Autofac.Extras.Quartz\packages\Quartz.2.6.0\lib\net40\Quartz.dll</HintPath>
+    </Reference>
     <Reference Include="System" />
     <Reference Include="System.configuration" />
     <Reference Include="System.Core" />
+    <Reference Include="System.DirectoryServices" />
+    <Reference Include="System.DirectoryServices.Linq, Version=1.2.2.1, Culture=neutral, PublicKeyToken=15767dc0c85f19f9, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.DirectoryServices.Linq.1.2.2.1\lib\net45\System.DirectoryServices.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System.Web" />
     <Reference Include="System.Web.Helpers, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
       <HintPath>..\packages\Microsoft.AspNet.WebPages.3.2.0\lib\net45\System.Web.Helpers.dll</HintPath>

+ 1 - 0
GreenTree.Nachtragsmanagement.Web.Framework/packages.config

@@ -6,4 +6,5 @@
   <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net452" />
   <package id="Microsoft.AspNet.WebPages" version="3.2.0" targetFramework="net452" />
   <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net452" />
+  <package id="System.DirectoryServices.Linq" version="1.2.2.1" targetFramework="net452" />
 </packages>

BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/info-16.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/info-24.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/search-16-contrast.png


BIN
GreenTree.Nachtragsmanagement.Web/Content/Images/search-16.png


+ 32 - 1
GreenTree.Nachtragsmanagement.Web/Controllers/AdminController.cs

@@ -90,6 +90,37 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             };
         }
 
+        /// <summary>
+        /// Partial edit for searching for users in ActiveDirectory
+        /// </summary>
+        /// <param name="searchValue">The text to be searched for.</param>
+        [HttpPost]
+        public ActionResult SearchUsers(string searchValue)
+        {
+            var users = _userService.SearchUsers(searchValue);
+
+            return PartialView("~/Views/Admin/Users/_UserSearchPartial.cshtml", users);
+        }
+
+        /// <summary>
+        /// Partial edit for selecting a specific user
+        /// </summary>
+        /// <param name="accountName">The user accountName.</param>
+        [HttpPost]
+        public ActionResult SelectUser(string accountName)
+        {
+            var user = _userService.SearchUsers(accountName)
+                .FirstOrDefault();
+
+            if (user == null)
+                return new EmptyResult();
+
+            return new JsonResult
+            {
+                Data = user
+            };
+        }
+
         /// <summary>
         /// Callback result for user grid
         /// </summary>
@@ -615,7 +646,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
                     return null;
 
                 var archive = ZipFile.Open(storePath, ZipArchiveMode.Read);
-                var applicationPath = Server.MapPath("/");
+                var applicationPath = Server.MapPath("~/");
 
                 archive.ExtractToDirectory(applicationPath, true);
 

+ 54 - 0
GreenTree.Nachtragsmanagement.Web/Controllers/GlobalController.cs

@@ -112,6 +112,60 @@ namespace GreenTree.Nachtragsmanagement.Web.Controllers
             return View("~/Views/Global/NotAuthorized.cshtml");
         }
 
+        /// <summary>
+        /// Shows a popup for changing the current user password
+        /// </summary>
+        public ActionResult ChangePassword()
+        {
+            var model = new PasswordChangeDataModel();
+
+            return View("~/Views/Shared/_ChangePasswordPartial.cshtml", model);
+        }
+
+        /// <summary>
+        /// Shows a popup for changing the current user password
+        /// </summary>
+        [HttpPost, ValidateInput(false)]
+        public ActionResult ChangePassword(PasswordChangeDataModel passwordChangeModel)
+        {
+            try
+            {
+                if (!ModelState.IsValid)
+                {
+                    passwordChangeModel.CurrentPassword = String.Empty;
+                    passwordChangeModel.NewPassword = String.Empty;
+                    passwordChangeModel.ConfirmedPassword = String.Empty;
+
+                    return PartialView("~/Views/Shared/_ChangePasswordPartial.cshtml", passwordChangeModel);
+                }
+
+                var currentUser = _userHelper.FromCookiesOrSession();
+
+                if (currentUser == null)
+                    throw new Exception("Kein Benutzer angemeldet.");
+
+                currentUser = _userService.GetUserById(currentUser.Id);
+
+                if (currentUser == null)
+                    throw new Exception("Angemeldeter Benutzer kann nicht gefunden werden.");
+
+                currentUser.Password = StaticHelper.GetMD5Hash(passwordChangeModel.NewPassword);
+
+                _userService.UpdateUser(currentUser);
+
+                return new JsonResult
+                {
+                    Data = "success"
+                };
+            }
+            catch (Exception ex)
+            {
+                _logger.Error("Fehler bei Änderung des Passworts.", ex, _userHelper.FromCookiesOrSession());
+
+                return PartialView("~/Views/Shared/_PopupError.cshtml", ex);
+            }
+        }
+
         /// <summary>
         /// Deletes the specified cookies and session variables from request
         /// </summary>

+ 16 - 15
GreenTree.Nachtragsmanagement.Web/Extensions/GridViewSettingsHelper.cs

@@ -1546,22 +1546,23 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             {
                 column.Caption = "Benachrichtigungs-Plugin";
                 column.FieldName = "NotificationPluginSystemNameDescription";
-                column.Width = new Unit(180, UnitType.Pixel);
+                column.MinWidth = 200;
+                column.Width = new Unit(280, UnitType.Pixel);
             });
             s.Columns.Add(column =>
             {
                 column.Caption = "Benachrichtigungs-Job";
                 column.FieldName = "NotificationJobSystemNameDescription";
                 column.PropertiesEdit.DisplayFormatString = "dd.MM.yyyy";
-                column.MinWidth = 180;
-                column.Width = new Unit(180, UnitType.Pixel);
+                column.MinWidth = 200;
+                column.Width = new Unit(280, UnitType.Pixel);
             });
             s.Columns.Add(column =>
             {
                 column.Caption = "Mitarbeiter";
                 column.FieldName = "UserDescriptions";
-                column.MinWidth = 150;
-                column.Width = new Unit(200, UnitType.Pixel);
+                column.MinWidth = 200;
+                column.Width = new Unit(250, UnitType.Pixel);
                 column.SetDataItemTemplateContent(c =>
                 {
                     var userDescriptions = DataBinder.Eval(c.DataItem, "UserDescriptions") as List<string>;
@@ -1589,8 +1590,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 column.Caption = "Nächste Ausführung";
                 column.FieldName = "NextExecutionTime";
                 column.PropertiesEdit.DisplayFormatString = "dd.MM.yyyy";
-                column.MinWidth = 110;
-                column.Width = new Unit(150, UnitType.Pixel);
+                column.MinWidth = 150;
+                column.Width = new Unit(200, UnitType.Pixel);
             });
 
             s.ClientLayout = (sender, e) =>
@@ -1717,8 +1718,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 });
                 column.Settings.AllowDragDrop = DefaultBoolean.False;
                 column.Settings.AllowSort = DefaultBoolean.False;
-                column.MinWidth = 100;
-                column.Width = new Unit(120, UnitType.Pixel);
+                column.MinWidth = 130;
+                column.Width = new Unit(160, UnitType.Pixel);
             });
             s.Columns.Add(column =>
             {
@@ -1732,8 +1733,8 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
             {
                 column.Caption = "Betreff";
                 column.FieldName = "ShortMessage";
-                column.MinWidth = 150;
-                column.Width = new Unit(150, UnitType.Pixel);
+                column.MinWidth = 200;
+                column.Width = new Unit(220, UnitType.Pixel);
                 column.SettingsHeaderFilter.Mode = GridHeaderFilterMode.CheckedList;
             });
             s.Columns.Add(column =>
@@ -1741,7 +1742,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 column.Caption = "Nachricht";
                 column.FieldName = "FullMessage";
                 column.MinWidth = 200;
-                column.Width = new Unit(200, UnitType.Pixel);
+                column.Width = new Unit(250, UnitType.Pixel);
                 column.SetDataItemTemplateContent(c =>
                 {
                     var fullMessage = DataBinder.Eval(c.DataItem, "FullMessage");
@@ -1768,7 +1769,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 column.Caption = "Mitarbeiter";
                 column.FieldName = "UserDescription";
                 column.MinWidth = 130;
-                column.Width = new Unit(130, UnitType.Pixel);
+                column.Width = new Unit(160, UnitType.Pixel);
                 column.SettingsHeaderFilter.Mode = GridHeaderFilterMode.CheckedList;
             });
             s.Columns.Add(column =>
@@ -1776,7 +1777,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 column.Caption = "Datentyp";
                 column.FieldName = "EntityType";
                 column.MinWidth = 100;
-                column.Width = new Unit(100, UnitType.Pixel);
+                column.Width = new Unit(130, UnitType.Pixel);
                 column.SettingsHeaderFilter.Mode = GridHeaderFilterMode.CheckedList;
             });
             s.Columns.Add(column =>
@@ -1809,7 +1810,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                 column.FieldName = "CreatedOnLocal";
                 column.PropertiesEdit.DisplayFormatString = "dd.MM.yyyy - HH:mm:ss";
                 column.MinWidth = 150;
-                column.Width = new Unit(150, UnitType.Pixel);
+                column.Width = new Unit(160, UnitType.Pixel);
             });
 
             s.ClientLayout = (sender, e) =>

+ 12 - 1
GreenTree.Nachtragsmanagement.Web/Extensions/ZipArchiveExtension.cs

@@ -33,7 +33,18 @@ namespace GreenTree.Nachtragsmanagement.Web.Extensions
                     Directory.CreateDirectory(directory);
 
                 if (file.Name != "")
-                    file.ExtractToFile(completeFileName, true);
+                {
+                    var memoryStream = file.Open();
+
+                    File.Delete(completeFileName);
+
+                    using (var fileStream = new FileStream(completeFileName, FileMode.CreateNew, FileAccess.ReadWrite))
+                    {
+                        memoryStream.CopyTo(fileStream);
+                    }
+
+                    //file.ExtractToFile(completeFileName, true);
+                }
             }
         }
     }

+ 3 - 0
GreenTree.Nachtragsmanagement.Web/Global.asax.cs

@@ -32,6 +32,7 @@ using GreenTree.Nachtragsmanagement.Services.Misc;
 using GreenTree.Nachtragsmanagement.Services.Scheduling;
 using GreenTree.Nachtragsmanagement.Services.Logging;
 using GreenTree.Nachtragsmanagement.Web.Extensions;
+using GreenTree.Nachtragsmanagement.Web.Update;
 
 namespace GreenTree.Nachtragsmanagement.Web
 {
@@ -73,6 +74,8 @@ namespace GreenTree.Nachtragsmanagement.Web
 
                 GridViewSettingsHelper._configurationService = Singleton<IContainer>.Instance.Resolve<IConfigurationService>();
 
+                new UpdateProcess().RunUpdateProcess();
+
                 logger.Information("Anwendung erfolgreich gestartet.");
             }
             catch (Exception ex)

+ 16 - 0
GreenTree.Nachtragsmanagement.Web/GreenTree.Nachtragsmanagement.Web.csproj

@@ -52,6 +52,9 @@
     <Reference Include="Autofac, Version=4.0.1.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
       <HintPath>..\packages\Autofac.4.0.1\lib\net45\Autofac.dll</HintPath>
     </Reference>
+    <Reference Include="Autofac.Extras.Quartz">
+      <HintPath>..\..\Autofac.Extras.Quartz\Autofac.Extras.Quartz\bin\Debug\Autofac.Extras.Quartz.dll</HintPath>
+    </Reference>
     <Reference Include="Common.Logging, Version=3.3.1.0, Culture=neutral, PublicKeyToken=af08829b84f0328e, processorArchitecture=MSIL">
       <HintPath>..\packages\Common.Logging.3.3.1\lib\net40\Common.Logging.dll</HintPath>
     </Reference>
@@ -89,6 +92,10 @@
     <Reference Include="System" />
     <Reference Include="System.ComponentModel.Composition" />
     <Reference Include="System.Data" />
+    <Reference Include="System.DirectoryServices" />
+    <Reference Include="System.DirectoryServices.Linq, Version=1.2.2.1, Culture=neutral, PublicKeyToken=15767dc0c85f19f9, processorArchitecture=MSIL">
+      <HintPath>..\packages\System.DirectoryServices.Linq.1.2.2.1\lib\net45\System.DirectoryServices.Linq.dll</HintPath>
+    </Reference>
     <Reference Include="System.Drawing" />
     <Reference Include="System.IO.Compression" />
     <Reference Include="System.IO.Compression.FileSystem" />
@@ -212,6 +219,10 @@
     <Content Include="Content\Images\function-Misc-32.png" />
     <Content Include="Content\Images\function-Misc-MailNotifications-32.png" />
     <Content Include="Content\Images\function-Administration-AppInfo-32.png" />
+    <Content Include="Content\Images\info-24.png" />
+    <Content Include="Content\Images\info-16.png" />
+    <Content Include="Content\Images\search-16-contrast.png" />
+    <Content Include="Content\Images\search-16.png" />
     <Content Include="Content\Images\logo.png" />
     <Content Include="Content\Images\maximize-16.png" />
     <Content Include="Content\Images\minimize-16.png" />
@@ -361,6 +372,8 @@
     <Content Include="Views\Misc\_HelpPageHtmlEditPartial.cshtml" />
     <Content Include="Views\Config\_ConfigItemReferenceEditPartial.cshtml" />
     <Content Include="Views\Admin\AppInfo\View.cshtml" />
+    <Content Include="Views\Admin\Users\_UserSearchPartial.cshtml" />
+    <Content Include="Views\Shared\_ChangePasswordPartial.cshtml" />
     <None Include="Web.Debug.config">
       <DependentUpon>Web.config</DependentUpon>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -389,6 +402,7 @@
     <Compile Include="Models\Appendix\OrderInvoiceCreatedDataModel.cs" />
     <Compile Include="Models\Config\ConfigItemDataModel.cs" />
     <Compile Include="Models\Global\EditEntityCommentModel.cs" />
+    <Compile Include="Models\Global\PasswordChangeDataModel.cs" />
     <Compile Include="Models\Global\PrintGridModel.cs" />
     <Compile Include="Models\Misc\HelpPageDataModel.cs" />
     <Compile Include="Models\Misc\LogDataModel.cs" />
@@ -432,6 +446,7 @@
     <Compile Include="Models\Global\OptionDialogModel.cs" />
     <Compile Include="Models\Site\SiteDataModel.cs" />
     <Compile Include="Models\Site\SiteTreeDataModel.cs" />
+    <Compile Include="Update\UpdateProcess.cs" />
     <Compile Include="Validation\Admin\User\RoleDataModelValidator.cs" />
     <Compile Include="Validation\Admin\User\UserDataModelValidator.cs" />
     <Compile Include="Models\Global\FooterModel.cs" />
@@ -450,6 +465,7 @@
     <Compile Include="Validation\Deviation\DisturbanceDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\StatusDataModelValidator.cs" />
     <Compile Include="Validation\Deviation\DeviationDataModelValidator.cs" />
+    <Compile Include="Validation\Global\PasswordChangeModelValidator.cs" />
     <Compile Include="Validation\Misc\HelpPageDataModelValidator.cs" />
     <Compile Include="Validation\Misc\MailNotificationDataModelValidator.cs" />
     <Compile Include="Validation\Site\SiteDataModelValidator.cs" />

+ 2 - 0
GreenTree.Nachtragsmanagement.Web/Implementations/AppendixNotificationPlugin.cs

@@ -13,6 +13,8 @@ using System.Net;
 using System.Globalization;
 using Quartz;
 using GreenTree.Nachtragsmanagement.Services.Logging;
+using Autofac;
+using GreenTree.Nachtragsmanagement.Core;
 
 namespace GreenTree.Nachtragsmanagement.Web.Implementations
 {

+ 1 - 0
GreenTree.Nachtragsmanagement.Web/Models/Admin/User/UserDataModel.cs

@@ -17,6 +17,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Models.Admin.User
         public string Lastname { get; set; }
         public string MailAddress { get; set; }
         public string Password { get; set; }
+        public bool IsActiveDirectory { get; set; }
         public ICollection<int> RoleValues { get; set; }
         public ICollection<string> RoleDescriptions { get; set; }
         public string RoleDescription

+ 14 - 0
GreenTree.Nachtragsmanagement.Web/Models/Global/PasswordChangeDataModel.cs

@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Models.Global
+{
+    public class PasswordChangeDataModel
+    {
+        public string CurrentPassword { get; set; }
+        public string NewPassword { get; set; }
+        public string ConfirmedPassword { get; set; }
+    }
+}

+ 106 - 0
GreenTree.Nachtragsmanagement.Web/Update/UpdateProcess.cs

@@ -0,0 +1,106 @@
+using Autofac;
+using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Domain.Misc;
+using GreenTree.Nachtragsmanagement.Data;
+using GreenTree.Nachtragsmanagement.Services.Logging;
+using GreenTree.Nachtragsmanagement.Services.User;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Update
+{
+    public class UpdateProcess
+    {
+        /// <summary>
+        /// Run all actions required for the current version
+        /// </summary>
+        public void RunUpdateProcess()
+        {
+            RunUpdateActionsForVersion(AppendixVersion.CurrentVersion);
+        }
+
+        /// <summary>
+        /// Run all actions required for a specific update version
+        /// </summary>
+        /// <param name="version">Update version.</param>
+        public void RunUpdateProcess(string version)
+        {
+            RunUpdateActionsForVersion(version);
+        }
+
+        /// <summary>
+        /// Run all actions for a specific update version
+        /// </summary>
+        /// <param name="version">Update version.</param>
+        private void RunUpdateActionsForVersion(string version)
+        {
+            if (String.IsNullOrEmpty(version)) return;
+
+            if (CheckExistingDbContextSpecForVersion(version)) return;
+
+            switch (version)
+            {
+                case "0.9.1.0":
+                    {
+                        var logger = Singleton<IContainer>.Instance.Resolve<IServiceLogger>();
+
+                        logger.Information("UpdateProcess Test - " + version);
+                    }
+                    break;
+                default:
+                    return;
+            }
+
+            CreateDbContextSpecForVersion(version);
+        }
+
+        /// <summary>
+        /// Checks the specified update version for an existing DbContextSpec entity to check wether the actions has already been done
+        /// </summary>
+        /// <param name="version">Update version.</param>
+        /// <returns>True when action already done otherwise False.</returns>
+        private bool CheckExistingDbContextSpecForVersion(string version)
+        {
+            if (String.IsNullOrEmpty(version)) return true;
+
+            var dbContext = Singleton<IContainer>.Instance.Resolve<IDbContext>();
+
+            if (dbContext == null) return true;
+
+            var versionSpecName = String.Format("UpdateRun-{0}", version);
+
+            var versionSpec = dbContext.Get<DbContextSpec>()
+                .FirstOrDefault(d => d.Name == versionSpecName);
+
+            if (versionSpec != null) return true;
+
+            return false;
+        }
+
+        /// <summary>
+        /// Sets a new DbContextSpec entity to determine wether the actions has already been done
+        /// </summary>
+        /// <param name="version">Update version.</param>
+        private void CreateDbContextSpecForVersion(string version)
+        {
+            if (String.IsNullOrEmpty(version)) return;
+
+            if (CheckExistingDbContextSpecForVersion(version)) return;
+
+            var dbContext = Singleton<IContainer>.Instance.Resolve<IDbContext>();
+
+            if (dbContext == null) return;
+
+            var versionSpec = new DbContextSpec
+            {
+                Name = String.Format("UpdateRun-{0}", version),
+                Value = "True"
+            };
+
+            dbContext.Get<DbContextSpec>().Add(versionSpec);
+            dbContext.SaveChanges();
+        }
+    }
+}

+ 1 - 1
GreenTree.Nachtragsmanagement.Web/Validation/Admin/User/UserDataModelValidator.cs

@@ -35,7 +35,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation.Admin.User
 
             RuleFor(m => m.Password)
                 .NotEmpty()
-                .When(p => p.Id == -1)
+                .When(p => p.Id == -1 && !p.IsActiveDirectory)
                     .WithMessage("Passwort wird benötigt");
         }
     }

+ 3 - 0
GreenTree.Nachtragsmanagement.Web/Validation/AppendixValidatorFactory.cs

@@ -3,12 +3,14 @@ using GreenTree.Nachtragsmanagement.Web.Models.Admin.User;
 using GreenTree.Nachtragsmanagement.Web.Models.Appendix;
 using GreenTree.Nachtragsmanagement.Web.Models.Config;
 using GreenTree.Nachtragsmanagement.Web.Models.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Models.Global;
 using GreenTree.Nachtragsmanagement.Web.Models.Misc;
 using GreenTree.Nachtragsmanagement.Web.Models.Site;
 using GreenTree.Nachtragsmanagement.Web.Validation.Admin.User;
 using GreenTree.Nachtragsmanagement.Web.Validation.Appendix;
 using GreenTree.Nachtragsmanagement.Web.Validation.Config;
 using GreenTree.Nachtragsmanagement.Web.Validation.Deviation;
+using GreenTree.Nachtragsmanagement.Web.Validation.Global;
 using GreenTree.Nachtragsmanagement.Web.Validation.Misc;
 using GreenTree.Nachtragsmanagement.Web.Validation.Site;
 using System;
@@ -38,6 +40,7 @@ namespace GreenTree.Nachtragsmanagement.Web.Validation
             validators.Add(typeof(IValidator<MailNotificationDataModel>), new MailNotificationDataModelValidator());
             validators.Add(typeof(IValidator<HelpPageDataModel>), new HelpPageDataModelValidator());
             validators.Add(typeof(IValidator<ConfigItemDataModel>), new ConfigItemDataModelValidator());
+            validators.Add(typeof(IValidator<PasswordChangeDataModel>), new PasswordChangeModelValidator());
         }
 
         public override IValidator CreateInstance(Type validatorType)

+ 52 - 0
GreenTree.Nachtragsmanagement.Web/Validation/Global/PasswordChangeModelValidator.cs

@@ -0,0 +1,52 @@
+using Autofac;
+using FluentValidation;
+using GreenTree.Nachtragsmanagement.Core;
+using GreenTree.Nachtragsmanagement.Core.Authentication;
+using GreenTree.Nachtragsmanagement.Services.User;
+using GreenTree.Nachtragsmanagement.Web.Models.Global;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Web;
+
+namespace GreenTree.Nachtragsmanagement.Web.Validation.Global
+{
+    public class PasswordChangeModelValidator : AbstractValidator<PasswordChangeDataModel>
+    {
+        public PasswordChangeModelValidator()
+        {
+            RuleFor(m => m.CurrentPassword)
+                .Must(m => CurrentPasswordIsCorrect(m))
+                    .WithMessage("Aktuelles Passwort ist falsch");
+
+            RuleFor(m => m.NewPassword)
+                .NotEmpty()
+                    .WithMessage("Ein neues Passwort wird benötigt");
+
+            RuleFor(m => m)
+                .Must(m => m.NewPassword == m.ConfirmedPassword)
+                    .WithMessage("Das neue und das bestätigte Passwort stimmen nicht überein");
+        }
+
+        private bool CurrentPasswordIsCorrect(string currentPassword)
+        {
+            var userHelper = Singleton<IContainer>.Instance.Resolve<IUserHelper>();
+            var userService = Singleton<IContainer>.Instance.Resolve<IUserService>();
+
+            var currentUser = userHelper.FromCookiesOrSession();
+
+            if (currentUser == null)
+                return false;
+
+            currentUser = userService.GetUserById(currentUser.Id);
+
+            if (currentUser == null)
+                return false;
+
+            if (currentUser.Password != StaticHelper.GetMD5Hash(currentPassword))
+                return false;
+
+            return true;
+        }
+    }
+}

+ 1 - 0
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/View.cshtml

@@ -89,6 +89,7 @@
 			}
 		});
 	}
+
 </script>
 
 @Html.Partial("~/Views/Admin/Users/_UserGridPartial.cshtml", Model)

+ 76 - 19
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserEditPartial.cshtml

@@ -35,19 +35,23 @@
 			updateSelectAllItemState();
 			updateText();
 		}
+
 		function updateSelectAllItemState() {
 			isAllSelected() ? RoleValues.SelectIndices([0]) : RoleValues.UnselectIndices([0]);
 		}
+
 		function isAllSelected() {
 			for (var i = 1; i < RoleValues.GetItemCount(); i++)
 				if (!RoleValues.GetItem(i).selected)
 					return false;
 			return true;
 		}
+
 		function updateText() {
 			var selectedItems = RoleValues.GetSelectedItems();
 			devDropDownListRoleValues.SetText(getSelectedItemsText(selectedItems));
 		}
+
 		function synchronizeListBoxValues(s, e) {
 			RoleValues.UnselectAll();
 			var texts = s.GetText().split(textSeparator);
@@ -56,6 +60,7 @@
 			updateSelectAllItemState();
 			updateText();
 		}
+
 		function getSelectedItemsText(items) {
 			var texts = [];
 			for (var i = 0; i < items.length; i++)
@@ -63,6 +68,7 @@
 					texts.push(items[i].text);
 			return texts.join(textSeparator);
 		}
+
 		function getValuesByTexts(texts) {
 			var actualValues = [];
 			var item;
@@ -73,6 +79,32 @@
 			}
 			return actualValues;
 		}
+
+		function onButtonEditClick(e) {
+			if (e.buttonIndex == 0) {
+				searchUsers();
+			}
+		}
+
+		function searchUsers() {
+			var searchVal = CustomNumber.GetText();
+			if (!searchVal) return;
+			$.ajax({
+				type: "POST",
+				url: '@Url.Action("SearchUsers", "Admin")',
+				data: { searchValue: searchVal },
+				success: function (response) {
+					setTimeout(function () {
+						$(".userSearchContainer").remove();
+						$("body").append(response);
+					}, 200);
+				},
+				error: function () {
+					 alert("error occured");
+				}
+			});
+		}
+
 	</script>
 
 	@Html.DevExpress().PopupControl(s =>
@@ -102,29 +134,37 @@
 			ViewContext.Writer.Write("<input type=\"hidden\" value=\"" + Model.Id + "\" id=\"Id\" name=\"Id\" />");
 
 			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CustomNumber, "Personalnummer:"));
-			Html.DevExpress().TextBoxFor(m => m.CustomNumber, t =>
+			Html.DevExpress().ButtonEditFor(m => m.CustomNumber, t =>
 			{
 				t.Width = new Unit(60, UnitType.Percentage);
+
+				var searchEditButton = new EditButton();
+				searchEditButton.Image.Url = Url.Content("~/Content/Images/search-16.png");
+				searchEditButton.Image.UrlHottracked = Url.Content("~/Content/Images/search-16-contrast.png");
+
+				t.Properties.Buttons.Add(searchEditButton);
+				t.Properties.ClientSideEvents.ButtonClick = "function (s, e) { onButtonEditClick(e); }";
+				t.Properties.ClientSideEvents.KeyPress = "function (s, e) { if (e.htmlEvent.keyCode == 13) { searchUsers(); } }";
 			}).Render();
 
 			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
-					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Forename, "Vorname:"));
-					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Forename).ToHtmlString());
-					Html.DevExpress().TextBoxFor(m => m.Forename, t =>
-					{
-						t.Width = new Unit(95, UnitType.Percentage);
-					}).Render();
-				ViewContext.Writer.Write("</div>");
-
-				ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
-					ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Lastname, "Nachname:"));
-					ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Lastname).ToHtmlString());
-					Html.DevExpress().TextBoxFor(m => m.Lastname, t =>
-					{
-						t.Width = new Unit(100, UnitType.Percentage);
-					}).Render();
-				ViewContext.Writer.Write("</div>");
+			ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Forename, "Vorname:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Forename).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.Forename, t =>
+			{
+				t.Width = new Unit(95, UnitType.Percentage);
+			}).Render();
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 50%'>");
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Lastname, "Nachname:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Lastname).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.Lastname, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+			}).Render();
+			ViewContext.Writer.Write("</div>");
 			ViewContext.Writer.Write("</div>");
 
 			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.MailAddress, "E-Mail:"));
@@ -134,13 +174,30 @@
 				t.Width = new Unit(100, UnitType.Percentage);
 			}).Render();
 
+			ViewContext.Writer.Write("<div class='inlineModelPropertyContainer'>");
+			ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 60%'>");
 			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.Password, "Passwort:"));
 			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.Password).ToHtmlString());
 			Html.DevExpress().TextBoxFor(m => m.Password, t =>
 			{
-				t.Width = new Unit(60, UnitType.Percentage);
+				t.Width = new Unit(98, UnitType.Percentage);
 				t.Properties.Password = true;
 			}).Render();
+			ViewContext.Writer.Write("</div>");
+
+			ViewContext.Writer.Write("<div class='inlineModelProperty' style='width: 40%'>");
+			ViewContext.Writer.Write(
+				"<img id=\"imgPasswordInfo\" src=\""
+				+ Url.Content("~/Content/Images/info-24.png") + "\" title=\"Bei AD-Benutzern wird kein Passwort verwendet\"" +
+				"style=\"display: none; margin-top: 35px\" />");
+
+			ViewContext.Writer.Write("</div>");
+			ViewContext.Writer.Write("</div>");
+
+			Html.DevExpress().CheckBoxFor(m => m.IsActiveDirectory, t =>
+			{
+				t.ClientVisible = false;
+			}).Render();
 
 			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.RoleValues, "Rollen:"));
 			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.RoleValues).ToHtmlString());

+ 82 - 0
GreenTree.Nachtragsmanagement.Web/Views/Admin/Users/_UserSearchPartial.cshtml

@@ -0,0 +1,82 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model IEnumerable<GreenTree.Nachtragsmanagement.Core.Authentication.ActiveDirectoryUser>
+
+<div class="userSearchContainer">
+	<script>
+
+		function selectUser() {
+			var searchVal = CustomNumber.GetText();
+			if (!searchVal) return;
+			$.ajax({
+				type: "POST",
+				url: '@Url.Action("SelectUser", "Admin")',
+				data: { accountName: devListBoxSearchUsers.GetValue() },
+				success: function (response) {
+					setTimeout(function () {
+						if (!response) return;
+						CustomNumber.SetText(response.AccountName);
+						Forename.SetText(response.ForeName);
+						Lastname.SetText(response.LastName);
+						MailAddress.SetText(response.Mail);
+						Password.SetEnabled(false);
+						$(Password.GetMainElement()).css("background", "#efefef");
+						$(Password.GetMainElement()).find("input").css("background", "#efefef");
+						IsActiveDirectory.SetChecked(true);
+						devPopupControlSearchUser.Hide();
+						$("#imgPasswordInfo").css("display", "block");
+					}, 200);
+				},
+				error: function () {
+					 alert("error occured");
+				}
+			});
+		}
+
+	</script>
+
+	@Html.DevExpress().PopupControl(s =>
+	{
+		s.Name = "devPopupControlSearchUser";
+		s.HeaderText = "Benutzersuche";
+		s.Modal = true;
+		s.Width = new Unit(280, UnitType.Pixel);
+		s.CloseAction = CloseAction.CloseButton;
+		s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+		s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+		s.AllowDragging = false;
+		s.AllowResize = false;
+		s.ShowFooter = false;
+		s.ShowOnPageLoad = true;
+		s.SetContent(() =>
+		{
+			using (Html.BeginForm("SelectUser", "Admin", FormMethod.Post, new { id = "userSelectForm" }))
+			{
+				ViewContext.Writer.Write("<div class='editFormWrapper'>");
+
+				ViewContext.Writer.Write("Suchergebnis:");
+				Html.DevExpress().ListBox(t =>
+				{
+					t.Name = "devListBoxSearchUsers";
+					t.Width = new Unit(100, UnitType.Percentage);
+					t.Height = new Unit(210, UnitType.Pixel);
+					t.Properties.ValueField = "AccountName";
+					t.Properties.TextField = "Name";
+				}).BindList(Model).Render();
+
+				ViewContext.Writer.Write("</div>");
+
+				Html.RenderPartial(
+					"~/Views/Shared/_PopupButtonPanel.cshtml",
+					new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+					{
+						PopupName = "devPopupControlSearchUser",
+						AcceptFunction = "function (s, e) { selectUser(); }"
+					}
+				);
+			}
+		});
+		s.Styles.Content.Paddings.Padding = new Unit(0);
+		s.Styles.ModalBackground.Opacity = 0;
+	}).GetHtml()
+</div>

+ 92 - 0
GreenTree.Nachtragsmanagement.Web/Views/Shared/_ChangePasswordPartial.cshtml

@@ -0,0 +1,92 @@
+@using GreenTree.Nachtragsmanagement.Web.Extensions
+
+@model GreenTree.Nachtragsmanagement.Web.Models.Global.PasswordChangeDataModel
+
+<div class="changePasswordContainer">
+
+	<script>
+
+		function changePassword() {
+			var form = $("#changePasswordEditForm");
+			$(form).submit(function (e) {
+				$.ajax({
+					type: "POST",
+					url: '@Url.Action("ChangePassword", "Global")',
+					data: form.serialize(),
+					success: function (response) {
+						setTimeout(function () {
+							$(".changePasswordContainer").remove();
+							if (response == "success") {
+								alert("Passwort erfolgreich geändert!");
+							} else {
+								$("body").append(response);
+							}
+						}, 200);
+					}
+				});
+				e.preventDefault();
+			});
+			form.submit();
+		}
+
+	</script>
+
+	<link rel="stylesheet" type="text/css" href="~/Content/function.css" />
+
+@Html.DevExpress().PopupControl(s =>
+{
+	s.Name = "devPopupControlChangePassword";
+	s.HeaderText = "Passwort ändern";
+	s.ShowFooter = false;
+	s.ShowMaximizeButton = false;
+	s.Modal = true;
+	s.ShowOnPageLoad = true;
+	s.Width = new Unit(280, UnitType.Pixel);
+	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
+	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
+	s.CloseAction = CloseAction.CloseButton;
+	s.SetContent(() =>
+	{
+		using (Html.BeginForm("ChangePassword", "Global", FormMethod.Post, new { id = "changePasswordEditForm" }))
+		{
+			ViewContext.Writer.Write("<div class='editFormWrapper'>");
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.CurrentPassword, "Aktuelles Passwort:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.CurrentPassword).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.CurrentPassword, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+				t.Properties.Password = true;
+			}).Render();
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.NewPassword, "Neues Passwort:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m.NewPassword).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.NewPassword, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+				t.Properties.Password = true;
+			}).Render();
+
+			ViewContext.Writer.Write(Html.CustomLabelFor(m => m.ConfirmedPassword, "Neues Passwort bestätigen:"));
+			ViewContext.Writer.Write(Html.ValidationMessageFor(m => m).ToHtmlString());
+			Html.DevExpress().TextBoxFor(m => m.ConfirmedPassword, t =>
+			{
+				t.Width = new Unit(100, UnitType.Percentage);
+				t.Properties.Password = true;
+			}).Render();
+
+			ViewContext.Writer.Write("</div>");
+
+			Html.RenderPartial(
+				"~/Views/Shared/_PopupButtonPanel.cshtml",
+				new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel
+				{
+					PopupName = "devPopupControlChangePassword",
+					AcceptFunction = "function (s, e) { changePassword(); }"
+				});
+		}
+	});
+	s.Styles.Content.Paddings.Padding = new Unit(0);
+}).GetHtml()
+
+</div>

+ 35 - 4
GreenTree.Nachtragsmanagement.Web/Views/Shared/_Footer.cshtml

@@ -4,30 +4,61 @@
 	function acceptFooterRoleSelection() {
 		window.location = "@Url.Action("SetRole", "Global")?roleId=" + devListBoxRoles.GetValue();
 	}
+
+	function showPasswordChange() {
+		$.ajax({
+			url: '@Url.Action("ChangePassword", "Global")',
+			success: function (response) {
+				setTimeout(function () {
+					$(".changePasswordContainer").remove();
+					$("body").append(response);
+				}, 200);
+			},
+			error: function () {
+				 alert("error occured");
+			}
+		});
+	}
 </script>
 
 @Html.DevExpress().PopupControl(s =>
 {
 	s.Name = "devPopupControlRoles";
-	s.HeaderText = "Rolle wechseln";
+	s.HeaderText = "Benutzermenü";
 	s.ShowFooter = false;
 	s.ShowMaximizeButton = false;
 	s.Modal = true;
-	s.Width = new Unit(350, UnitType.Pixel);
+	s.Width = new Unit(300, UnitType.Pixel);
 	s.PopupHorizontalAlign = PopupHorizontalAlign.WindowCenter;
 	s.PopupVerticalAlign = PopupVerticalAlign.WindowCenter;
 	s.CloseAction = CloseAction.CloseButton;
 	s.SetContent(() =>
 	{
+		ViewContext.Writer.Write("<div style=\"padding: 0 13px\">");
+		ViewContext.Writer.Write("<div>Rollen wechseln</div>");
 		Html.DevExpress().ListBox(l =>
 		{
 			l.Name = "devListBoxRoles";
 			l.Properties.TextField = "Description";
 			l.Properties.ValueField = "ID";
 			l.Width = new Unit(100, UnitType.Percentage);
-			l.Height = new Unit(250, UnitType.Pixel);
-			l.Properties.RootStyle.Border.BorderStyle = BorderStyle.None;
+			l.Height = new Unit(150, UnitType.Pixel);
+			l.ControlStyle.BorderLeft.BorderStyle = BorderStyle.None;
+			l.ControlStyle.BorderRight.BorderStyle = BorderStyle.None;
 		}).BindList(ViewData["Roles"]).GetHtml();
+
+		ViewContext.Writer.Write("<div style=\"margin: 8px 0\">");
+		Html.DevExpress().Button(t =>
+		{
+			t.Name = "devButtonChangePassword";
+			t.Text = "Eigenes Passwort ändern";
+			t.UseSubmitBehavior = false;
+			t.RenderMode = ButtonRenderMode.Link;
+			t.ClientSideEvents.Click = "function (s, e) { showPasswordChange(); }";
+		}).Render();
+		ViewContext.Writer.Write("</div>");
+		ViewContext.Writer.Write("</div>");
+
 		Html.RenderPartial(
 			"~/Views/Shared/_PopupButtonPanel.cshtml",
 			new GreenTree.Nachtragsmanagement.Web.Models.Global.PopupModel

+ 4 - 3
GreenTree.Nachtragsmanagement.Web/Web.config

@@ -31,7 +31,7 @@
     <appendixConfigSection iisAnonymousUser="IUSR" checkUpdateUrl="https://greentreestudios.de/AppUpdate/api/Update/CheckUpdate" updateStorePath="~/App_Data/UpdatePackages">
       <mailServer smtpServer="greentreestudios.de" port="25" username="a.diekmann@greentreestudios.de" domain="" password="14595809ad." useSsl="false">
       </mailServer>
-      <ldapServer ldapServer="" administriveUser="" password="">
+      <ldapServer ldapConnectionString="LDAP://PORTAIT.int/DC=portait,DC=int" administriveUser="ADadmin" password="adadmin920">
       </ldapServer>
     </appendixConfigSection>
   </appendixSectionGroup>
@@ -62,7 +62,8 @@
         <add assembly="DevExpress.XtraGauges.v17.1.Core, Version=17.1.4.0, Culture=neutral, PublicKeyToken=b88d1754d700e49a" />
       </assemblies>
     </compilation>
-    <authentication mode="None" />
+    <!--<authentication mode="None" />-->
+    <authentication mode="Windows" />
     <pages validateRequest="true" clientIDMode="Predictable">
       <namespaces>
         <add namespace="System.Web.Helpers" />
@@ -93,7 +94,7 @@
     </httpModules>
     <globalization culture="" uiCulture="" />
     <httpRuntime maxRequestLength="4096" requestValidationMode="4.0" executionTimeout="110" targetFramework="4.5.2" />
-    <sessionState timeout="120"  />
+    <sessionState timeout="120" />
   </system.web>
   <system.webServer>
     <validation validateIntegratedModeConfiguration="false" />

+ 1 - 0
GreenTree.Nachtragsmanagement.Web/packages.config

@@ -30,6 +30,7 @@
   <package id="System.Diagnostics.Debug" version="4.3.0" targetFramework="net452" />
   <package id="System.Diagnostics.Tools" version="4.3.0" targetFramework="net452" />
   <package id="System.Diagnostics.Tracing" version="4.3.0" targetFramework="net452" />
+  <package id="System.DirectoryServices.Linq" version="1.2.2.1" targetFramework="net452" />
   <package id="System.Globalization" version="4.3.0" targetFramework="net452" />
   <package id="System.IO" version="4.3.0" targetFramework="net452" />
   <package id="System.IO.Compression" version="4.3.0" targetFramework="net452" />