SSO GH plugin

We have a GH plugin with a couple hundred components, all of them inherit an abstract class which then inherits GH_Component (that might be helpful).
What would be the easiest way to add a single sing-on support (MSAL in that case), which is required to run our plugin ?
I already tried disabling the components when signing fails, removing the data from the parameters and so on… but all of these solutions result in more problems down the line.

There are so many possibilities to this. What I would do is to create a singleton which deals with managing authentication.

It could implement a event system to activate/deactivate computation or it just provides a bunch of internal flags.

In theory you could escape or throw any public method depending if your authentication is invalid.

Throwing an NotAuthenticated exception is probably the best. If you don’t want to do this for any component or even public method, you could just global unload or break the dll after a given time from your authentication manager. Another option is to override the beforeSolution (?) and throw inside there. But if you just deal with GH_Components, people might just reference your .gha and call the methods by script.

Also these options are not super safe, but for the majority of users…

A simple example (Console app)… Did it help?

using System;

namespace AuthExample
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                AuthenticationManager.Instance.RequestAuthentication();
                try
                {
                    Console.WriteLine("Please enter any integer");
                    for (int i = 0; i < 3; i++)
                    {
                        if (int.TryParse(Console.ReadLine(), out int number))
                        {
                            MySuperAwesomeCode(number);
                            break;
                        }
                        Console.WriteLine("Weirdo input. Please enter again...");
                    }
                }
                catch (NotAuthenticatedException ex)
                {
                    Console.WriteLine(ex.Message);
                }
            }         
        }

        /// <summary>
        /// :)
        /// </summary>
        static void MySuperAwesomeCode(int number)
        {
            AuthenticationManager.Instance.CheckState(requestAuthentication: false);
            Console.WriteLine("Thanks for paying me :). Now I'm tell you if this number is odd...");

            if (number % 2 == 1)
            {
                Console.WriteLine("Yes it is");
            }
            else
            {
                Console.WriteLine("No sorry...");
            }
        }
    }

    /// <summary>
    /// Thread safe singleton implementation of an object responsible to manage user authentication 
    /// </summary>
    public sealed class AuthenticationManager
    {
        private static readonly Lazy<AuthenticationManager> _lazy = new Lazy<AuthenticationManager>(() => new AuthenticationManager());
        private bool _isAuthenticated = false; 

        public static AuthenticationManager Instance { get { return _lazy.Value; } }

        private AuthenticationManager()
        {
        }

        /// <summary>
        /// Checks the authentication state
        /// </summary>
        /// <exception cref="NotAuthenticatedException">Gets thrown if the user is not authenticated</exception>
        /// <param name="requestAuthentication"></param>
        public void CheckState(bool requestAuthentication = false)
        {
            if (!_isAuthenticated)
            {
                if (requestAuthentication)
                {
                    //todo: request authentication 
                    RequestAuthentication();
                    CheckState(requestAuthentication: false);
                }               
                throw new NotAuthenticatedException();
            }
        }

        /// <summary>
        /// Request the user to input his credentials (or in this case a simple password)
        /// </summary>
        public void RequestAuthentication()
        {
            Console.WriteLine("What is the password? (its 'cheesecake' !)");
            string input = Console.ReadLine();
            if (input.ToLowerInvariant() == "cheesecake")
            {
                _isAuthenticated = true;
            }
        }
    }

    /// <summary>
    /// Can be thrown if the user is not authenticated
    /// </summary>
    public class NotAuthenticatedException : Exception
    {
        public NotAuthenticatedException() : base("Cannot proceed further. You are not authenticated")
        {          
        }
    }
}

That is the part I have already. The problem is how to integrate this into GH plugin, without putting the auth manager call in literally hundreds of components within the SolveInstance void.

Activating/Deactivating by locking the solver can be bypassed easily…

Inheritance or Extentions methods. As you said, you have an abstract class in between your components and the GH_Component base class. In that abstraction I would do the check and throw on failure in the override of base.BeforeSolveInstance() method. If an authentication failed, you throw and then your component becomes red, even before anything is computed. Isn’t that what you want?

The advantage of using a singleton or service is that you can call this check in one line from anywhere in your code base. As I said, I would even implement this line as often as possible, since otherwise people could do library calls using script components. But if you don’t want this, the upper approach should already work…

This would be the equivalent in a script component:

EDIT: Now I see that throwing in these method might be catched and the computation still takes place although the component is red. This is actually weird

Instead of throwing you could remove the component from the document with GrasshopperDocument.Objects.Remove(Component);

But seriously, it would be better to separate concerns. I believe a GH_Component should just be a wrapper. I don’t like the idea of directly writing model logic inside the SolveInstance/Runscript. If its referencing to a well made api instead, making the api protective would be a much simpler and cleaner solution. And it open ways in referencing the library by code (if authenticated).

Me.OnPingDocument().RequestAbortSolution() placed in BeforeSolveInstance() works fine and seems least destructive.

1 Like

Great, btw this is how you bypass :slight_smile: :