Attribute providers (Legacy)

Attribute providers (Legacy)

ℹ️
This topic refers to legacy configuration syntax.

Attribute Providers enables the Maverics Orchestrator to act as a source of user attributes. Attribute Providers can be leveraged to enhance a user’s profile with metadata from multiple sources to provide a richer user experience.

ℹ️
This feature is experimental and hence requires a feature flag to be enabled in order to leverage Attribute Providers in the Maverics Orchestrator. The feature flag name is attrproviders.

Available types of Attribute Providers

LDAP Attribute Provider

LDAP Attribute Provider creates an LDAP server allowing client connections via TLS. This is different from the LDAP connector which acts as a client to query external LDAP sources.

The LDAP Attribute Provider supports the following LDAP requests:

StartTLS

StartTLS upgrades the TCP connection to TLS using the provided tls config. LDAP clients may leverage StartTLS to secure their connection in case the LDAP provider has been configured with ldap:// vs ldaps://.

Bind

Bind uses the authenticateSE Service Extension to authenticate the LDAP connection. Currently, the Bind operation only supports Simple Authentication method.

Unbind

Unbind signals to close the session’s connection.

Search

Search uses the query Service Extension to consolidate and filter data sources, returning results back to the client.

Service Extensions for Attribute Providers

Use the authenticateSE Service Extension to determine if the provided credentials are valid and authorize a new session.

Use the query Service Extension to query the attribute provider. A series of responses may be sent back to the client finishing up with a SearchDoneResponse.

Configuration

Required

The attrproviders configuration must define authenticateSE and query Service Extensions.

uri is required and must start with either ldap:// or ldaps://. If using ldaps then the configuration must define and use tls.

Defaults

Default ports will be used if unspecified in uri: ldap:// defaulting to 389 or ldaps:// defaulting to 636.

Optional

anonymousBind is a boolean field which should be set to true if LDAP search is performed without binding.

Defining an LDAP Attribute Provider in config

tls:
  ldapTLS:
    certFile: /etc/maverics/certs/ldap.crt
    keyFile: /etc/maverics/certs/ldap.key
  maverics:
    certFile: /etc/maverics/certs/maverics.crt
    keyFile: /etc/maverics/certs/maverics.key

http:
  address: :443
  tls: maverics

features:
  attrproviders: true

connectors:
  - name: azure
    type: azure
    graphURL: https://graph.microsoft.com
    authType: oidc
    oidcWellKnownURL: https://login.microsoftonline.com/<tenantID>/v2.0/.well-known/openid-configuration
    oauthClientID: client-id
    oauthClientSecret: <client-secret>
    oauthRedirectURL: https://example.com/oidc

attrproviders:
  - name: LDAPAttrProvider
    type: ldap
    uri: ldaps://ldap.example.com:636
    tls: ldapTLS
    anonymousBind: false
    authenticate:
      funcName: Authenticate
      file: /etc/maverics/extensions/authenticate.go

    query:
      funcName: Query
      file: /etc/maverics/extensions/query.go

/etc/maverics/extensions/authenticate.go

package main

import (
	"fmt"
	"strings"

	"maverics/log"
)

func Authenticate(username, password string) (bool, error) {
	log.Info("LDAPAttrProvider authenticating")
	if strings.HasPrefix(username, "admin") {
		log.Info(fmt.Sprintf("'%s' has been authenticated", username))
		return true, nil
	}
	return false, nil
}

/etc/maverics/extensions/query.go

package main

import (
	"fmt"
	"strings"

	"maverics/attr"
)

func Query(lap *attr.LDAPProvider, dn string, filter string, reqAttrs []string) (map[string]map[string]interface{}, error) {
	// Parse out the username in filter, e.g. "([email protected])".
	filter = strings.Trim(filter, "()")
	filterV := strings.Split(filter, "=")

	filters := make(map[string]string)
	filters["username"] = filterV[1]

	// Request attributes from the Azure IDP using a provided username filter.
	azureAttrs, err := lap.IDPs["azure"].Query(filters, reqAttrs)
	if err != nil {
		return nil, fmt.Errorf("unable to query attributes from azure: %w", err)
	}

	// Convert from map[string]string to map[string]interface{}.
	vv := make(map[string]interface{})
	for k, v := range azureAttrs {
		vv[k] = v
	}

	result := make(map[string]map[string]interface{})
	result["[email protected]"] = vv
	return result, nil
}

Additional LDAP Resources

  1. https://datatracker.ietf.org/doc/html/rfc4513
  2. https://datatracker.ietf.org/doc/html/rfc4511