Direct entry in the 1C: Enterprise directory through Linq as an example of working with Asp.Net users

  • Tutorial

Direct entry in the 1C: Enterprise directory through Linq as an example of working with Asp.Net users


The article describes how to register a website user in the directory 1C: Enterprise 8, located in the MSSQL database. Further, the user can log in to the site using the username and password specified during registration. Work is carried out only with a directory with the name Users and does not affect the system of working with users through the 1C configurator.

Only the simplest registration and authorization operations are described. Auxiliary operations for password recovery, informing via E-mail are not covered. The work is conducted by direct access to the MSSQL database through Linq. The approach allows you to use both the Asp.Net and 1C functionality, as well as dispense with intermediaries in the form of different CMS.



Creating a directory Users in 1C: Enterprise


It is assumed that the 1C information base has been created and is located on the MSSQL server on the local network or remotely from the hosting provider.

It is necessary to create the Users directory through the configurator with a code in the form of a string of 9 characters and a name of 25 characters.

Creating a directory Users in 1C: Enterprise

The field structure is as follows (the list of fields is taken in abundance for further expansion of functionality):
  • Active: Boolean
  • Registration Date: Date
  • Email: Line 255, Variable
  • Password: line 32 variable
  • Password Prefix: String 16 Variable
  • Language: String 255 Variable
  • FormatDates: String 20 Variable
  • Activation Code: String 36 Variable
  • Password Recovery Code: Line 36 variable
  • Activity Date: Date


The interface of the created directory is immediately accessible from 1C, which saves time on creating the administration interface. This is one of the advantages of the method when the entire arsenal of 1C: Enterprise tools is available. Other advantages include: marking for deletion without physical deletion, filters and search, the ability to build ACS queries, exchange with other 1C databases through data conversion, etc.

For example, a list of registered users is available immediately after defining the directory in the configurator:

A list of registered users is available immediately after determining the directory in the configurator

As well as the user editing form:

User edit form interface

Configuring direct access to the MSSQL database



To access the data, you need to generate a cs file with LINQ definitions. For generation, you need to use the LinqTo1C utility, specifying which directories you need to unload, as well as the connection string to the MSSQL database. For our case, it is enough to unload only the Directory. Users.

Starting with version 1.2, LinqTo1C allows you to create a configuration file with the ability to modify data. Despite the advantages of direct writing to the database, the method has disadvantages. For example, information recorded in this way will not be automatically recorded in exchange plans. You yourself need to take care of maintaining the correctness of the data: generate a unique code, maintain integrity with other objects, check the newly entered data.

LinqTo1C Utility

As a result of the utility, 2 files will appear: dbml - for visual presentation and cs-for adding to a C # project. It looks like this in the Visual Studio editor:

The result of LinqTo1C in Visual Studio

Asp.Net code for registration and authorization


The code is for Asp.Net MVC, where AccountController is responsible for the user’s work.

AccountController:
public ActionResult LogOn()
        {
            ViewBag.Title = Resources.Account.LogonTitle;
            return View("~/Views/Dotnet/Logon.cshtml");
        }
        [HttpPost]
        public ActionResult LogOn(LogOnModel model, string returnUrl)
        {
            if (ModelState.IsValid)
            {
                if (MembershipService.ValidateUser(model.UserName, model.Password))
                {
                    FormsService.SignIn(model.UserName, model.RememberMe);
                    if (Url.IsLocalUrl(returnUrl))
                    {
                        return Redirect(returnUrl);
                    }
                    else
                    {
                        return RedirectToAction("Index", "Dotnet");
                    }
                }
                else
                {
                    ModelState.AddModelError("", "");
                }
            }
            // If we got this far, something failed, redisplay form
            return View("~/Views/Dotnet/Logon.cshtml", model);
        }
        public ActionResult LogOff()
        {
            FormsService.SignOut();
            return RedirectToAction("Index", "Dotnet");
        }
        public ActionResult Register()
        {
            ViewBag.Title = Resources.Account.RegisterTitle;
            ViewBag.PasswordLength = MembershipService.MinPasswordLength;
            return View("~/Views/Dotnet/Register.cshtml");
        }
        [HttpPost]
        public ActionResult Register(RegisterModel model)
        {
            if (ModelState.IsValid)
            {
                // Attempt to register the user
                MembershipCreateStatus createStatus = MembershipService.CreateUser(model.UserName, model.Email, model.Password, model.ConfirmPassword);
                if (createStatus == MembershipCreateStatus.Success)
                {
                    FormsService.SignIn(model.UserName, false /* createPersistentCookie */);
                    return RedirectToAction("Index", "Dotnet");
                }
                else
                {
                    ModelState.AddModelError("", AccountValidation.ErrorCodeToString(createStatus));
                }
            }
            // If we got this far, something failed, redisplay form
            ViewBag.PasswordLength = MembershipService.MinPasswordLength;
            return View("~/Views/Dotnet/Register.cshtml", model);
        }


Calls species FormsService.SignOutand FormsService.SignInare not as interesting as they are redirected to the standard methods: FormsAuthentication.SignOutand FormsAuthentication.SetAuthCookie.

The class AccountMembershipServiceaccesses the MSSQL base.

The method is intended for user registration CreateUser. First, possible errors are checked: empty login, password, e-mail, duplicate login / e-mail, password match and password confirmation. Next, the user fields are filled. The link is assigned a new Guid, the number is looked up as the maximum number + 1. The password is stored as a checksum in a closed one, the checksum is calculated from the user's password and a unique prefix, which is stored here in the record.
public MembershipCreateStatus CreateUser(string userName, string email, string password, string confirmPassword)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
            if (String.IsNullOrEmpty(email)) throw new ArgumentException("Value cannot be null or empty.", "email");
            MembershipCreateStatus status = MembershipCreateStatus.ProviderError;
            using (var dataContext = new ElisyCMS(ConfigurationManager.ConnectionStrings["ElisyCMS"].ConnectionString))
            {
                if (dataContext.СправочникПользователи.Where(m => m.Наименование == userName && m.ПометкаУдаления == new Binary(new byte[]{0})).Count() != 0)
                    return MembershipCreateStatus.DuplicateUserName;
                if (dataContext.СправочникПользователи.Where(m => m.Email == email && m.ПометкаУдаления == new Binary(new byte[] { 0 })).Count() != 0)
                    return MembershipCreateStatus.DuplicateEmail;
                if (password != confirmPassword)
                    return MembershipCreateStatus.InvalidPassword;
                try
                {
                    СправочникПользователи user = new СправочникПользователи();
                    user.Ссылка = Guid.NewGuid().ToByteArray();
                    user.ПометкаУдаления = new byte[] { 0 };
                    user.Предопределенный = new byte[] { 0 };
                    var codeRequest = from a in dataContext.СправочникПользователи
                                      where Convert.ToInt32(a.Код) > 0
                                      orderby Convert.ToInt32(a.Код) descending
                                      select Convert.ToInt32(a.Код);
                    var lastCode = codeRequest.Take(1).FirstOrDefault();
                    user.Код = (lastCode + 1).ToString().PadLeft(9, '0');
                    user.Наименование = userName;
                    byte[] saltBytes = new byte[8];
                    new RNGCryptoServiceProvider().GetBytes(saltBytes);
                    user.ПарольПрефикс = Convert.ToBase64String(saltBytes);
                    byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(user.ПарольПрефикс + password);
                    byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(passwordBytes);
                    user.Пароль = Convert.ToBase64String(hash);
                    user.ДатаРегистрации = DateTime.Now;
                    user.ДатаАктивности = DateTime.Now;
                    user.Активный = new Binary(new byte[] { 1 });
                    user.Язык = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
                    //user.КодАктивации = Guid.NewGuid().ToString();
                    user.Email = email;
                    dataContext.СправочникПользователи.InsertOnSubmit(user);
                    dataContext.SubmitChanges();
                    return MembershipCreateStatus.Success;
                }
                catch (Exception ex)
                {
                    return MembershipCreateStatus.ProviderError;
                }
            }
            return status;
        }
          

The ValidateUser method is used for authorization. After checking the parameters, it checks the user’s presence in the database, the activity flag and tries to compare the checksum of the password with the calculated checksum of the transmitted password and a unique prefix.
        public bool ValidateUser(string userName, string password)
        {
            if (String.IsNullOrEmpty(userName)) throw new ArgumentException("Value cannot be null or empty.", "userName");
            if (String.IsNullOrEmpty(password)) throw new ArgumentException("Value cannot be null or empty.", "password");
            using (var dataContext = new ElisyCMS(ConfigurationManager.ConnectionStrings["ElisyCMS"].ConnectionString))
            {
                var пользователь = dataContext.СправочникПользователи.Where(m => m.Наименование.ToUpper() == userName.ToUpper()).FirstOrDefault();
                if (пользователь == null)
                    return false;
                if (пользователь.Активный.ToArray()[0] == 0)
                    return false;
                if (String.IsNullOrWhiteSpace(пользователь.Пароль))
                    return true;
                byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(пользователь.ПарольПрефикс + password);
                byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(passwordBytes);
                return Convert.ToBase64String(hash).Equals(пользователь.Пароль);
            }
        }

Also popular now: