github.com/greenpau/go-authcrunch@v1.1.4/pkg/ids/local/store.go (about)

     1  // Copyright 2022 Paul Greenberg greenpau@outlook.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package local
    16  
    17  import (
    18  	"encoding/json"
    19  
    20  	"github.com/greenpau/go-authcrunch/pkg/authn/enums/operator"
    21  	"github.com/greenpau/go-authcrunch/pkg/authn/icons"
    22  	"github.com/greenpau/go-authcrunch/pkg/errors"
    23  	"github.com/greenpau/go-authcrunch/pkg/requests"
    24  	"go.uber.org/zap"
    25  )
    26  
    27  const (
    28  	storeKind = "local"
    29  )
    30  
    31  // Config holds the configuration for the identity store.
    32  type Config struct {
    33  	Name  string  `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"`
    34  	Realm string  `json:"realm,omitempty" xml:"realm,omitempty" yaml:"realm,omitempty"`
    35  	Path  string  `json:"path,omitempty" xml:"path,omitempty" yaml:"path,omitempty"`
    36  	Users []*User `json:"users,omitempty" xml:"users,omitempty" yaml:"users,omitempty"`
    37  
    38  	// LoginIcon is the UI login icon attributes.
    39  	LoginIcon *icons.LoginIcon `json:"login_icon,omitempty" xml:"login_icon,omitempty" yaml:"login_icon,omitempty"`
    40  
    41  	// RegistrationEnabled controls whether visitors can registers.
    42  	RegistrationEnabled bool `json:"registration_enabled,omitempty" xml:"registration_enabled,omitempty" yaml:"registration_enabled,omitempty"`
    43  	// UsernameRecoveryEnabled controls whether a user could recover username by providing an email address.
    44  	UsernameRecoveryEnabled bool `json:"username_recovery_enabled,omitempty" xml:"username_recovery_enabled,omitempty" yaml:"username_recovery_enabled,omitempty"`
    45  	// PasswordRecoveryEnabled controls whether a user could recover password by providing an email address.
    46  	PasswordRecoveryEnabled bool `json:"password_recovery_enabled,omitempty" xml:"password_recovery_enabled,omitempty" yaml:"password_recovery_enabled,omitempty"`
    47  	// ContactSupportEnabled controls whether contact support link is available.
    48  	ContactSupportEnabled bool `json:"contact_support_enabled,omitempty" xml:"contact_support_enabled,omitempty" yaml:"contact_support_enabled,omitempty"`
    49  
    50  	// SupportLink is the link to the support portal.
    51  	SupportLink string `json:"support_link,omitempty" xml:"support_link,omitempty" yaml:"support_link,omitempty"`
    52  	// SupportEmail is the email address to reach support.
    53  	SupportEmail string `json:"support_email,omitempty" xml:"support_email,omitempty" yaml:"support_email,omitempty"`
    54  }
    55  
    56  // IdentityStore represents authentication provider with local identity store.
    57  type IdentityStore struct {
    58  	config        *Config        `json:"-"`
    59  	authenticator *Authenticator `json:"-"`
    60  	logger        *zap.Logger
    61  	configured    bool
    62  }
    63  
    64  // NewIdentityStore return an instance of AuthDB-based identity store.
    65  func NewIdentityStore(cfg *Config, logger *zap.Logger) (*IdentityStore, error) {
    66  	if logger == nil {
    67  		return nil, errors.ErrIdentityStoreConfigureLoggerNotFound
    68  	}
    69  
    70  	b := &IdentityStore{
    71  		config: cfg,
    72  		logger: logger,
    73  	}
    74  
    75  	// Configure UI login icon.
    76  	if b.config.LoginIcon == nil {
    77  		b.config.LoginIcon = icons.NewLoginIcon(storeKind)
    78  	} else {
    79  		b.config.LoginIcon.Configure(storeKind)
    80  	}
    81  
    82  	if err := b.config.Validate(); err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	return b, nil
    87  }
    88  
    89  // GetRealm return authentication realm.
    90  func (b *IdentityStore) GetRealm() string {
    91  	return b.config.Realm
    92  }
    93  
    94  // GetName return the name associated with this identity store.
    95  func (b *IdentityStore) GetName() string {
    96  	return b.config.Name
    97  }
    98  
    99  // GetKind returns the authentication method associated with this identity store.
   100  func (b *IdentityStore) GetKind() string {
   101  	return storeKind
   102  }
   103  
   104  // Configured returns true if the identity store was configured.
   105  func (b *IdentityStore) Configured() bool {
   106  	return b.configured
   107  }
   108  
   109  // Request performs the requested identity store operation.
   110  func (b *IdentityStore) Request(op operator.Type, r *requests.Request) error {
   111  	switch op {
   112  	case operator.Authenticate:
   113  		return b.Authenticate(r)
   114  	case operator.IdentifyUser:
   115  		return b.authenticator.IdentifyUser(r)
   116  	case operator.ChangePassword:
   117  		return b.authenticator.ChangePassword(r)
   118  	case operator.AddKeySSH:
   119  		return b.authenticator.AddPublicKey(r)
   120  	case operator.AddKeyGPG:
   121  		return b.authenticator.AddPublicKey(r)
   122  	case operator.DeletePublicKey:
   123  		return b.authenticator.DeletePublicKey(r)
   124  	case operator.AddMfaToken:
   125  		return b.authenticator.AddMfaToken(r)
   126  	case operator.DeleteMfaToken:
   127  		return b.authenticator.DeleteMfaToken(r)
   128  	case operator.AddAPIKey:
   129  		return b.authenticator.AddAPIKey(r)
   130  	case operator.DeleteAPIKey:
   131  		return b.authenticator.DeleteAPIKey(r)
   132  	case operator.GetPublicKeys:
   133  		return b.authenticator.GetPublicKeys(r)
   134  	case operator.GetPublicKey:
   135  		return b.authenticator.GetPublicKey(r)
   136  	case operator.GetAPIKeys:
   137  		return b.authenticator.GetAPIKeys(r)
   138  	case operator.GetAPIKey:
   139  		return b.authenticator.GetAPIKey(r)
   140  	case operator.GetMfaTokens:
   141  		return b.authenticator.GetMfaTokens(r)
   142  	case operator.GetMfaToken:
   143  		return b.authenticator.GetMfaToken(r)
   144  	case operator.AddUser:
   145  		return b.authenticator.AddUser(r)
   146  	case operator.GetUsers:
   147  		return b.authenticator.GetUsers(r)
   148  	case operator.GetUser:
   149  		return b.authenticator.GetUser(r)
   150  	case operator.DeleteUser:
   151  		return b.authenticator.DeleteUser(r)
   152  	case operator.LookupAPIKey:
   153  		return b.authenticator.LookupAPIKey(r)
   154  	}
   155  
   156  	b.logger.Error(
   157  		"detected unsupported identity store operation",
   158  		zap.Any("op", op),
   159  		zap.Any("params", r),
   160  	)
   161  	return errors.ErrOperatorNotSupported.WithArgs(op)
   162  }
   163  
   164  // Configure configures IdentityStore.
   165  func (b *IdentityStore) Configure() error {
   166  	if b.authenticator == nil {
   167  		b.authenticator = NewAuthenticator()
   168  	}
   169  	b.authenticator.logger = b.logger
   170  
   171  	if err := b.authenticator.Configure(b.config.Path, b.config.Users); err != nil {
   172  		return err
   173  	}
   174  
   175  	b.logger.Info(
   176  		"successfully configured identity store",
   177  		zap.String("name", b.config.Name),
   178  		zap.String("kind", storeKind),
   179  		zap.String("db_path", b.config.Path),
   180  		zap.Any("login_icon", b.config.LoginIcon),
   181  	)
   182  
   183  	b.configured = true
   184  	return nil
   185  }
   186  
   187  // GetConfig returns IdentityStore configuration.
   188  func (b *IdentityStore) GetConfig() map[string]interface{} {
   189  	var m map[string]interface{}
   190  	j, _ := json.Marshal(b.config)
   191  	json.Unmarshal(j, &m)
   192  	return m
   193  }
   194  
   195  // Authenticate performs authentication.
   196  func (b *IdentityStore) Authenticate(r *requests.Request) error {
   197  	if err := b.authenticator.AuthenticateUser(r); err != nil {
   198  		return errors.ErrIdentityStoreLocalAuthFailed.WithArgs(err)
   199  	}
   200  	return nil
   201  }
   202  
   203  // Validate validates identity store configuration.
   204  func (cfg *Config) Validate() error {
   205  	if cfg.Name == "" {
   206  		return errors.ErrIdentityStoreConfigureNameEmpty
   207  	}
   208  	if cfg.Realm == "" {
   209  		return errors.ErrIdentityStoreConfigureRealmEmpty
   210  	}
   211  	if cfg.Path == "" {
   212  		return errors.ErrIdentityStoreLocalConfigurePathEmpty
   213  	}
   214  	return nil
   215  }
   216  
   217  // GetLoginIcon returns the instance of the icon associated with the provider.
   218  func (b *IdentityStore) GetLoginIcon() *icons.LoginIcon {
   219  	// Add support and credentials recovery to the UI login icon.
   220  	b.config.LoginIcon.RegistrationEnabled = b.config.RegistrationEnabled
   221  	b.config.LoginIcon.UsernameRecoveryEnabled = b.config.UsernameRecoveryEnabled
   222  	b.config.LoginIcon.PasswordRecoveryEnabled = b.config.PasswordRecoveryEnabled
   223  	b.config.LoginIcon.ContactSupportEnabled = b.config.ContactSupportEnabled
   224  	b.config.LoginIcon.SupportLink = b.config.SupportLink
   225  	b.config.LoginIcon.SupportEmail = b.config.SupportEmail
   226  	return b.config.LoginIcon
   227  }