github.com/greenpau/go-authcrunch@v1.0.50/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  	"github.com/greenpau/go-authcrunch/pkg/authn/enums/operator"
    20  	"github.com/greenpau/go-authcrunch/pkg/authn/icons"
    21  	"github.com/greenpau/go-authcrunch/pkg/errors"
    22  	"github.com/greenpau/go-authcrunch/pkg/requests"
    23  	"go.uber.org/zap"
    24  )
    25  
    26  const (
    27  	storeKind = "local"
    28  )
    29  
    30  // Config holds the configuration for the identity store.
    31  type Config struct {
    32  	Name  string  `json:"name,omitempty" xml:"name,omitempty" yaml:"name,omitempty"`
    33  	Realm string  `json:"realm,omitempty" xml:"realm,omitempty" yaml:"realm,omitempty"`
    34  	Path  string  `json:"path,omitempty" xml:"path,omitempty" yaml:"path,omitempty"`
    35  	Users []*User `json:"users,omitempty" xml:"users,omitempty" yaml:"users,omitempty"`
    36  
    37  	// LoginIcon is the UI login icon attributes.
    38  	LoginIcon *icons.LoginIcon `json:"login_icon,omitempty" xml:"login_icon,omitempty" yaml:"login_icon,omitempty"`
    39  
    40  	// RegistrationEnabled controls whether visitors can registers.
    41  	RegistrationEnabled bool `json:"registration_enabled,omitempty" xml:"registration_enabled,omitempty" yaml:"registration_enabled,omitempty"`
    42  	// UsernameRecoveryEnabled controls whether a user could recover username by providing an email address.
    43  	UsernameRecoveryEnabled bool `json:"username_recovery_enabled,omitempty" xml:"username_recovery_enabled,omitempty" yaml:"username_recovery_enabled,omitempty"`
    44  	// PasswordRecoveryEnabled controls whether a user could recover password by providing an email address.
    45  	PasswordRecoveryEnabled bool `json:"password_recovery_enabled,omitempty" xml:"password_recovery_enabled,omitempty" yaml:"password_recovery_enabled,omitempty"`
    46  	// ContactSupportEnabled controls whether contact support link is available.
    47  	ContactSupportEnabled bool `json:"contact_support_enabled,omitempty" xml:"contact_support_enabled,omitempty" yaml:"contact_support_enabled,omitempty"`
    48  
    49  	// SupportLink is the link to the support portal.
    50  	SupportLink string `json:"support_link,omitempty" xml:"support_link,omitempty" yaml:"support_link,omitempty"`
    51  	// SupportEmail is the email address to reach support.
    52  	SupportEmail string `json:"support_email,omitempty" xml:"support_email,omitempty" yaml:"support_email,omitempty"`
    53  }
    54  
    55  // IdentityStore represents authentication provider with local identity store.
    56  type IdentityStore struct {
    57  	config        *Config        `json:"-"`
    58  	authenticator *Authenticator `json:"-"`
    59  	logger        *zap.Logger
    60  	configured    bool
    61  }
    62  
    63  // NewIdentityStore return an instance of AuthDB-based identity store.
    64  func NewIdentityStore(cfg *Config, logger *zap.Logger) (*IdentityStore, error) {
    65  	if logger == nil {
    66  		return nil, errors.ErrIdentityStoreConfigureLoggerNotFound
    67  	}
    68  
    69  	b := &IdentityStore{
    70  		config: cfg,
    71  		logger: logger,
    72  	}
    73  
    74  	// Configure UI login icon.
    75  	if b.config.LoginIcon == nil {
    76  		b.config.LoginIcon = icons.NewLoginIcon(storeKind)
    77  	} else {
    78  		b.config.LoginIcon.Configure(storeKind)
    79  	}
    80  
    81  	if err := b.config.Validate(); err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	return b, nil
    86  }
    87  
    88  // GetRealm return authentication realm.
    89  func (b *IdentityStore) GetRealm() string {
    90  	return b.config.Realm
    91  }
    92  
    93  // GetName return the name associated with this identity store.
    94  func (b *IdentityStore) GetName() string {
    95  	return b.config.Name
    96  }
    97  
    98  // GetKind returns the authentication method associated with this identity store.
    99  func (b *IdentityStore) GetKind() string {
   100  	return storeKind
   101  }
   102  
   103  // Configured returns true if the identity store was configured.
   104  func (b *IdentityStore) Configured() bool {
   105  	return b.configured
   106  }
   107  
   108  // Request performs the requested identity store operation.
   109  func (b *IdentityStore) Request(op operator.Type, r *requests.Request) error {
   110  	switch op {
   111  	case operator.Authenticate:
   112  		return b.Authenticate(r)
   113  	case operator.IdentifyUser:
   114  		return b.authenticator.IdentifyUser(r)
   115  	case operator.ChangePassword:
   116  		return b.authenticator.ChangePassword(r)
   117  	case operator.AddKeySSH:
   118  		return b.authenticator.AddPublicKey(r)
   119  	case operator.AddKeyGPG:
   120  		return b.authenticator.AddPublicKey(r)
   121  	case operator.DeletePublicKey:
   122  		return b.authenticator.DeletePublicKey(r)
   123  	case operator.AddMfaToken:
   124  		// b.logger.Debug("detected supported identity store operation", zap.Any("op", op), zap.Any("params", r))
   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.GetAPIKeys:
   135  		return b.authenticator.GetAPIKeys(r)
   136  	case operator.GetMfaTokens:
   137  		return b.authenticator.GetMfaTokens(r)
   138  	case operator.AddUser:
   139  		return b.authenticator.AddUser(r)
   140  	case operator.GetUsers:
   141  		return b.authenticator.GetUsers(r)
   142  	case operator.GetUser:
   143  		return b.authenticator.GetUser(r)
   144  	case operator.DeleteUser:
   145  		return b.authenticator.DeleteUser(r)
   146  	case operator.LookupAPIKey:
   147  		return b.authenticator.LookupAPIKey(r)
   148  	}
   149  
   150  	b.logger.Error(
   151  		"detected unsupported identity store operation",
   152  		zap.Any("op", op),
   153  		zap.Any("params", r),
   154  	)
   155  	return errors.ErrOperatorNotSupported.WithArgs(op)
   156  }
   157  
   158  // Configure configures IdentityStore.
   159  func (b *IdentityStore) Configure() error {
   160  	if b.authenticator == nil {
   161  		b.authenticator = NewAuthenticator()
   162  	}
   163  	b.authenticator.logger = b.logger
   164  
   165  	if err := b.authenticator.Configure(b.config.Path, b.config.Users); err != nil {
   166  		return err
   167  	}
   168  
   169  	b.logger.Info(
   170  		"successfully configured identity store",
   171  		zap.String("name", b.config.Name),
   172  		zap.String("kind", storeKind),
   173  		zap.String("db_path", b.config.Path),
   174  		zap.Any("login_icon", b.config.LoginIcon),
   175  	)
   176  
   177  	b.configured = true
   178  	return nil
   179  }
   180  
   181  // GetConfig returns IdentityStore configuration.
   182  func (b *IdentityStore) GetConfig() map[string]interface{} {
   183  	var m map[string]interface{}
   184  	j, _ := json.Marshal(b.config)
   185  	json.Unmarshal(j, &m)
   186  	return m
   187  }
   188  
   189  // Authenticate performs authentication.
   190  func (b *IdentityStore) Authenticate(r *requests.Request) error {
   191  	if err := b.authenticator.AuthenticateUser(r); err != nil {
   192  		return errors.ErrIdentityStoreLocalAuthFailed.WithArgs(err)
   193  	}
   194  	return nil
   195  }
   196  
   197  // Validate validates identity store configuration.
   198  func (cfg *Config) Validate() error {
   199  	if cfg.Name == "" {
   200  		return errors.ErrIdentityStoreConfigureNameEmpty
   201  	}
   202  	if cfg.Realm == "" {
   203  		return errors.ErrIdentityStoreConfigureRealmEmpty
   204  	}
   205  	if cfg.Path == "" {
   206  		return errors.ErrIdentityStoreLocalConfigurePathEmpty
   207  	}
   208  	return nil
   209  }
   210  
   211  // GetLoginIcon returns the instance of the icon associated with the provider.
   212  func (b *IdentityStore) GetLoginIcon() *icons.LoginIcon {
   213  	// Add support and credentials recovery to the UI login icon.
   214  	b.config.LoginIcon.RegistrationEnabled = b.config.RegistrationEnabled
   215  	b.config.LoginIcon.UsernameRecoveryEnabled = b.config.UsernameRecoveryEnabled
   216  	b.config.LoginIcon.PasswordRecoveryEnabled = b.config.PasswordRecoveryEnabled
   217  	b.config.LoginIcon.ContactSupportEnabled = b.config.ContactSupportEnabled
   218  	b.config.LoginIcon.SupportLink = b.config.SupportLink
   219  	b.config.LoginIcon.SupportEmail = b.config.SupportEmail
   220  	return b.config.LoginIcon
   221  }