You are viewing limited content. For full access, please sign in.

Question

Question

[SDK] Need help with Logonuser

SDK
asked on June 25, 2018

Hi all,

 

Using Workflow, I make a script to copy a file from LF to my Windows.

This is my script

namespace WorkflowActivity.Scripting.Script
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using Laserfiche.DocumentServices;
    using Laserfiche.RepositoryAccess;

    /// <summary>
    /// Offre une ou plusieurs méthodes qui peuvent être exécutées au moment de l'exécution de l'activité de scriptage du flux de travail.
    /// </summary>
    public class Script1 : RAScriptClass102
    {
        /// <summary>
        /// Cette méthode est exécutée quand l'activité est effectuée.
        /// </summary>
        protected override void Execute()
        {

                                    //ISHARING
                                    Laserfiche.DocumentServices.DocumentExporter DocEx = new Laserfiche.DocumentServices.DocumentExporter();
                                    DocumentInfo DI = Document.GetDocumentInfo(this.BoundEntryId, RASession);
                                    DocEx.ExportElecDoc(DI, Convert.ToString(GetTokenValue("Fichier")));



        }
    }
}

Now I want to past this file to my NAS. I should login-on first but I don't know how to write it in my C#.

 

I read somewhere I should use logonuser but I don't really know how to do.

I'm not pretty good with SDK.

Someone can help?

 

Thanks in advance

Regards

0 0

Replies

replied on June 25, 2018

Your script will run as the Workflow service user account, so the easiest solution would be to grant that account access to your NAS.  If that is not an option, you need to call Windows functions to change the account that the current thread is running as.  LogonUser is one of them, and then you can impersonate the account.  This thread talks about this in the context of authenticating to Laserfiche server, but the idea and the code is the same for whatever operation you want to perform as that user.  And as this isn't particular to Laserfiche or the SDK you can use any relevant answers from Stack Overflow (or wherever).

Note that your script will need a username and password to use.  It's tricky to get it to work while not hard-coding those credentials in your script, but you can use things like environment variables to achieve that.

2 0
replied on June 26, 2018

Hi Brian,

 

Yes I already read this article and try to add the code in mine but I have an error.

This is my code

namespace WorkflowActivity.Scripting.Script
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using Laserfiche.DocumentServices;
    using Laserfiche.RepositoryAccess;

    /// <summary>
    /// Offre une ou plusieurs méthodes qui peuvent être exécutées au moment de l'exécution de l'activité de scriptage du flux de travail.
    /// </summary>
    public class Script1 : RAScriptClass102
    {
        /// <summary>
        /// Cette méthode est exécutée quand l'activité est effectuée.
        /// </summary>
        protected override void Execute()
        {

            IntPtr tokenPtr = IntPtr.Zero;
            bool loggedOn = LogonUser(user, domain, password, (int)LOGON_TYPE.LOGON32_LOGON_BATCH, (int)LOGON_PROVIDER.LOGON32_PROVIDER_DEFAULT, out tokenPtr);

            if (!loggedOn)
            throw new Win32Exception();

            WindowsImpersonationContext ctx = null;
            try
                {
                    ctx = WindowsIdentity.Impersonate(tokenPtr);
                    sess.LogIn(repoReg); // this is RA, LFSO would call LFConnection.Create()
                }
            catch (Exception ex)
                {
                    if (ctx != null) ctx.Undo();
                    CloseHandle(tokenPtr);
                    throw ex;
                }

            if (ctx != null) ctx.Undo();
            if (tokenPtr != IntPtr.Zero)
            CloseHandle(tokenPtr);

            //ISHARING
            Laserfiche.DocumentServices.DocumentExporter DocEx = new Laserfiche.DocumentServices.DocumentExporter();
            DocumentInfo DI = Document.GetDocumentInfo(this.BoundEntryId, RASession);
            DocEx.ExportElecDoc(DI, Convert.ToString(GetTokenValue("Fichier")));



        }
    }
}

And the error is : The name 'logoUser' does not exist in the current context,...

 

0 0
replied on June 26, 2018

Yes, you need to declare it first: https://stackoverflow.com/a/17217167/

You'll want to read up on pinvoke so that you know what is going on here, it's not your everyday .Net programming.

0 0
replied on June 26, 2018

Brian,

 

I'm not pretty good.

This is my script

namespace WorkflowActivity.Scripting.Script2
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Security.Permissions;
    using Laserfiche.DocumentServices;
    using Laserfiche.RepositoryAccess;

    /// <summary>
    /// Offre une ou plusieurs méthodes qui peuvent être exécutées au moment de l'exécution de l'activité de scriptage du flux de travail.
    /// </summary>
    ///
    ///
    ///
    class Program
        {
            // obtains user token
            [DllImport("advapi32.dll", SetLastError = true)]
            public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

            // closes open handes returned by LogonUser
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            public extern static bool CloseHandle(IntPtr handle);

            public void DoWorkUnderImpersonation()
                {
                    //elevate privileges before doing file copy to handle domain security
                    WindowsImpersonationContext impersonationContext = null;
                    IntPtr userHandle = IntPtr.Zero;
                    const int LOGON32_PROVIDER_DEFAULT = 0;
                    const int LOGON32_LOGON_INTERACTIVE = 2;
                    //string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
                    //string user = ConfigurationManager.AppSettings["ImpersonationUser"];
                    //string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
                    string domain = "ISHARING";
                    string user = "Olivier";
                    string password = "pass";

                    try
                        {
                            Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);

                            // if domain name was blank, assume local machine
                            if (domain == "")
                            domain = System.Environment.MachineName;

                            // Call LogonUser to get a token for the user
                            bool loggedOn = LogonUser(user,
                                        domain,
                                        password,
                                        LOGON32_LOGON_INTERACTIVE,
                                        LOGON32_PROVIDER_DEFAULT,
                                        ref userHandle);

                            if (!loggedOn)
                                {
                                    Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
                                    return;
                                }

                            // Begin impersonating the user
                            impersonationContext = WindowsIdentity.Impersonate(userHandle);

                            Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);

                            //run the program with elevated privileges (like file copying from a domain server)
                            DoWork();

                        }
                    catch (Exception ex)
                        {
                            Console.WriteLine("Exception impersonating user: " + ex.Message);
                        }
                    finally
                        {
                            // Clean up
                            if (impersonationContext != null)
                                {
                                    impersonationContext.Undo();
                                }

                            if (userHandle != IntPtr.Zero)
                                {
                                    CloseHandle(userHandle);
                                }
                        }
                }
            private void DoWork()
                {
                    //string[] files = System.IO.Directory.GetFiles(@"\\domainserver\e$\images", "*.jpg");
                    //ISHARING
                    Laserfiche.DocumentServices.DocumentExporter DocEx = new Laserfiche.DocumentServices.DocumentExporter();
                    DocumentInfo DI = Document.GetDocumentInfo(this.BoundEntryId, RASession);
                    DocEx.ExportElecDoc(DI, Convert.ToString(GetTokenValue("Fichier")));

                }
        }
}

I know I should write "public class Script1 : RAScriptClass102" somewhere but IDK where.

0 0
replied on June 26, 2018

You want that instead of "class Program", like how you have it in the first version above.

0 0
replied on June 28, 2018 Show version history

Hi Brian,

 

True, I made a lot of version.

this is the last one.

 

namespace WorkflowActivity.Scripting.Script
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using Laserfiche.DocumentServices;
    using Laserfiche.RepositoryAccess;
    using System.Runtime.InteropServices;
    using System.Security;


    /// <summary>
    /// Offre une ou plusieurs méthodes qui peuvent être exécutées au moment de l'exécution de l'activité de scriptage du flux de travail.
    /// </summary>
    ///
    ///
    /// public class ImpersonationDemo
{
    [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
    public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
        int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    public extern static bool CloseHandle(IntPtr handle);

    // Test harness.
    // If you incorporate this code into a DLL, be sure to demand FullTrust.
    [PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
    public static void Main(string[] args)
    {
        SafeTokenHandle safeTokenHandle;
        try
        {
            string userName, domainName;
            // Get the user token for the specified user, domain, and password using the
            // unmanaged LogonUser method.
            // The local machine name can be used for the domain name to impersonate a user on this machine.
            Console.Write("Enter the name of the domain on which to log on: ");
            domainName = Console.ReadLine();

            Console.Write("Enter the login of a user on {0} that you wish to impersonate: ", domainName);
            userName = Console.ReadLine();

            Console.Write("Enter the password for {0}: ", userName);

            const int LOGON32_PROVIDER_DEFAULT = 0;
            //This parameter causes LogonUser to create a primary token.
            const int LOGON32_LOGON_INTERACTIVE = 2;

            // Call LogonUser to obtain a handle to an access token.
            bool returnValue = LogonUser(userName, domainName, Console.ReadLine(),
                LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
                out safeTokenHandle);

            Console.WriteLine("LogonUser called.");

            if (false == returnValue)
            {
                int ret = Marshal.GetLastWin32Error();
                Console.WriteLine("LogonUser failed with error code : {0}", ret);
                throw new System.ComponentModel.Win32Exception(ret);
            }
            using (safeTokenHandle)
            {
                Console.WriteLine("Did LogonUser Succeed? " + (returnValue ? "Yes" : "No"));
                Console.WriteLine("Value of Windows NT token: " + safeTokenHandle);

                // Check the identity.
                Console.WriteLine("Before impersonation: "
                    + WindowsIdentity.GetCurrent().Name);
                // Use the token handle returned by LogonUser.
                using (WindowsIdentity newId = new WindowsIdentity(safeTokenHandle.DangerousGetHandle()))
                {
                    using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
                    {

                        // Check the identity.
                        Console.WriteLine("After impersonation: "
                            + WindowsIdentity.GetCurrent().Name);
                    }
                }
                // Releasing the context object stops the impersonation
                // Check the identity.
                Console.WriteLine("After closing the context: " + WindowsIdentity.GetCurrent().Name);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Exception occurred. " + ex.Message);
        }

    }
}
    public class Script1 : RAScriptClass102
    {
        /// <summary>
        /// Cette méthode est exécutée quand l'activité est effectuée.
        /// </summary>
        protected override void Execute()
        {



            //ISHARING
            Laserfiche.DocumentServices.DocumentExporter DocEx = new Laserfiche.DocumentServices.DocumentExporter();
            DocumentInfo DI = Document.GetDocumentInfo(this.BoundEntryId, RASession);
            DocEx.ExportElecDoc(DI, Convert.ToString(GetTokenValue("Fichier")));



        }
    }
}

 

replied on June 28, 2018

Hi Brian,

 

Something like that?

 

namespace WorkflowActivity.Scripting.Script2
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
    using System.Security.Permissions;
    using Laserfiche.DocumentServices;
    using Laserfiche.RepositoryAccess;

    /// <summary>
    /// Offre une ou plusieurs méthodes qui peuvent être exécutées au moment de l'exécution de l'activité de scriptage du flux de travail.
    /// </summary>
    ///
    ///
    ///
    private void DoWork()
        {
            // obtains user token
            [DllImport("advapi32.dll", SetLastError = true)]
            public static extern bool LogonUser(string pszUsername, string pszDomain, string pszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

            // closes open handes returned by LogonUser
            [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
            public extern static bool CloseHandle(IntPtr handle);

            public void DoWorkUnderImpersonation()
                {
                    //elevate privileges before doing file copy to handle domain security
                    WindowsImpersonationContext impersonationContext = null;
                    IntPtr userHandle = IntPtr.Zero;
                    const int LOGON32_PROVIDER_DEFAULT = 0;
                    const int LOGON32_LOGON_INTERACTIVE = 2;
                    //string domain = ConfigurationManager.AppSettings["ImpersonationDomain"];
                    //string user = ConfigurationManager.AppSettings["ImpersonationUser"];
                    //string password = ConfigurationManager.AppSettings["ImpersonationPassword"];
                    string domain = "ISHARING";
                    string user = "Olivier";
                    string password = "pass";

                    try
                        {
                            Console.WriteLine("windows identify before impersonation: " + WindowsIdentity.GetCurrent().Name);

                            // if domain name was blank, assume local machine
                            if (domain == "")
                            domain = System.Environment.MachineName;

                            // Call LogonUser to get a token for the user
                            bool loggedOn = LogonUser(user,
                                        domain,
                                        password,
                                        LOGON32_LOGON_INTERACTIVE,
                                        LOGON32_PROVIDER_DEFAULT,
                                        ref userHandle);

                            if (!loggedOn)
                                {
                                    Console.WriteLine("Exception impersonating user, error code: " + Marshal.GetLastWin32Error());
                                    return;
                                }

                            // Begin impersonating the user
                            impersonationContext = WindowsIdentity.Impersonate(userHandle);

                            Console.WriteLine("Main() windows identify after impersonation: " + WindowsIdentity.GetCurrent().Name);

                            //run the program with elevated privileges (like file copying from a domain server)
                            DoWork();
                            //string[] files = System.IO.Directory.GetFiles(@"\\domainserver\e$\images", "*.jpg");
                            //ISHARING
                            Laserfiche.DocumentServices.DocumentExporter DocEx = new Laserfiche.DocumentServices.DocumentExporter();
                            DocumentInfo DI = Document.GetDocumentInfo(this.BoundEntryId, RASession);
                            DocEx.ExportElecDoc(DI, Convert.ToString(GetTokenValue("Fichier")));


                        }
                    catch (Exception ex)
                        {
                            Console.WriteLine("Exception impersonating user: " + ex.Message);
                        }
                    finally
                        {
                            // Clean up
                            if (impersonationContext != null)
                                {
                                    impersonationContext.Undo();
                                }

                            if (userHandle != IntPtr.Zero)
                                {
                                    CloseHandle(userHandle);
                                }
                        }
                }
        }
}

 

0 0
replied on June 28, 2018

Line 21 should say:

public class Script1 : RAScriptClass102

0 0
replied on June 28, 2018

Brian,

 

I changed the Line 21 and get this error :

Error 1 'WorkflowActivity.Scripting.Script2.Script1' does not implement the abstract inherited member 'WorkflowActivity.Scripting.ScriptBase.Execute ()' \ Script \ Script 1.cs 21
0 0
replied on June 28, 2018

You probably want to rename DoWorkUnderImpersonation to Execute.  You call a function DoWork that doesn't exist, you'll want to define that and do your work (copying files?) in there.

0 0
replied on June 28, 2018

Have you considered just giving WF access to your NAS?

1 0
replied on June 28, 2018

How can I giving WF access to my NAS?

0 0
replied on June 28, 2018

Your script will run as the Workflow service user account, so the easiest solution would be to grant that account access to your NAS (or change the account to one that has access).  Then just use the UNC path.

1 0
You are not allowed to follow up in this post.

Sign in to reply to this post.