Skip to content

Legend Authentication

Ephrim Stanley edited this page Oct 17, 2022 · 11 revisions

Overview

This doc is a proposal for refactoring the Legend metamodel (and related impl detail) wrto authentication.

Current Challenges

  • Many ways to do the same thing - A concept can be described in many ways. This increases cognitive load for users and also duplication of Legend code and effort.

In both cases a private key is loaded from a vault ; though there are some differences.

Class AwsPKAuthenticationStrategy extends AuthenticationStrategy
{
    secretArn: String[1];
    user: String[1];
}

Class SnowflakePublicAuthenticationStrategy extends AuthenticationStrategy
{
    privateKeyVaultReference : String[1];
    passPhraseVaultReference : String[1];
    publicUserName : String[1];
}
  • Reusable components buried in specific modules The SnowflakePublicAuthenticationStrategy can be used by any component that needs to obtain an encrypted private key. But using this strategy requires adding a dependency on the relational module.

  • No consistent way to describe authn Any Legend component that connects to an external "data source" needs some form of authentication. While there might be many data sources there are a relatively small number of authentication schemes that (I think) can be consistently described and used across the platform.

  • Split authentication from credential acquisition Authentication is the process by which a system verifies the identity of a "user". E.g a database authenticates the user making a connection.

Authentication requires that "user" present one/more "credentials". Credentials might be explicitly acquired (e.g read a key/pair from a vault) or might be acquired from the runtime environment (e.g current Kerberos subject).

To complete authentication, credentials have to be presented to the target system using some protocol. E.g add credentials to a JDBC properties object, create a file on disk that can be read by the ODBC driver, add a HTTP header with a bearer token etc.

While the authentication protocol by itself is very data source specific, the credential acquisition is more general and can be reused.

Proposal

  1. Identify a grammar to consistently describe credential acquisition
  2. Identify a grammar to consistently describe credential acquisition in the context of a connection
  3. Define metamodels for 1 and 2. Add Maven modules to legend-engine for 1 and 2
  4. Implement reusable credential acquisition code and add to legend-engine as a Maven module.

Implementation Considerations

  • We might not find the perfect abstraction. Very specific authn impls should be housed where they are used.
  • Authn is relatively heavy in terms of Java library dependencies. How can we prevent these dependencies from leaking all over the place ? For e.g should we shade/relocate all third party dependencies ?
  • Other projects that use Legend need to be able to extend credential acquisition behavior. At the minimum they should be able to define and load their own implementations.
  • How can we make the new metamodel coexist with the old metatmodel, migration path etc.

Implementation Sketches

A few sketches.

Relational.

# Current 
RelationalDatabaseConnection my::conn
{
   store: my::store,
   type: Snowflake,
   specification: Snowflake {
       name: db1,
       account1: account1,
   },
  auth: SnowflakePublic {
          userName: a
          privateKeyVaultReference: b 
          privateKeyPassPhraseVaultReference: c
   }
}

# Proposed

RelationalDatabaseConnection my::conn
{
   store: my::store,
   type: Snowflake,
   specification: Snowflake {
       name: db1,
       account1: account1,
   },
   auth: [   <-------------------- auth schemes supported by this connection 
             SnowflakePublic {   <------ on auth scheme
                userName: a
                privateKey: PrivateKeyCredential { <----------- credential
                   vault: aws/secretsmanager
                   reference: arn:1234
                }
                passPhrase: PlainTextCredential{
                  vault: aws/secretsmanager
                  reference: arn:4567
                }
        }
  ] 
}

Service store.

Current 

ServiceStoreConnection my::conn
{
   store: my::store,
   baseUrl: http://data.com
}

Proposed 

ServiceStoreConnection my::conn
{
   store: my::store,
   baseUrl: http://data.com,
   auth: [ <-------- service store is a little tricky. Unlike Relational, ServiceStore declares the "schemes" it supports. The auth model needs to carry enough metadata that we can validate if a configured auth is supported by the ServiceStore
      {    
        GSSSO {},  
        mTLS {   
           clientKeyStore : JavaKeyStoreCredential {
               keyStorePath : /local/certs/api1.jks
           },
           caKeyStore: JavaKeyStoreCredential {
               keyStorePath : /local/certs/ca.jks
           }
        },
        APIKey {
           key : myKey
           value : PlainTextCredential {
              vault: aws/secretsmanager,
              reference : arn:1234
           }
        }             
      }
   ]
}

Persistence 

Mastery 


Prototype Code

https://github.com/epsstan/legend-engine/tree/generic-authn/legend-engine-xt-credentials