github.com/cs3org/reva/v2@v2.27.7/pkg/siteacc/data/account.go (about)

     1  // Copyright 2018-2020 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this filePath 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  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package data
    20  
    21  import (
    22  	"strings"
    23  	"time"
    24  
    25  	"github.com/cs3org/reva/v2/pkg/siteacc/credentials"
    26  	"github.com/pkg/errors"
    27  
    28  	"github.com/cs3org/reva/v2/pkg/utils"
    29  )
    30  
    31  // Account represents a single site account.
    32  type Account struct {
    33  	Email       string `json:"email"`
    34  	Title       string `json:"title"`
    35  	FirstName   string `json:"firstName"`
    36  	LastName    string `json:"lastName"`
    37  	Site        string `json:"site"`
    38  	Role        string `json:"role"`
    39  	PhoneNumber string `json:"phoneNumber"`
    40  
    41  	Password credentials.Password `json:"password"`
    42  
    43  	DateCreated  time.Time `json:"dateCreated"`
    44  	DateModified time.Time `json:"dateModified"`
    45  
    46  	Data     AccountData     `json:"data"`
    47  	Settings AccountSettings `json:"settings"`
    48  }
    49  
    50  // AccountData holds additional data for a site account.
    51  type AccountData struct {
    52  	GOCDBAccess bool `json:"gocdbAccess"`
    53  	SiteAccess  bool `json:"siteAccess"`
    54  }
    55  
    56  // AccountSettings holds additional settings for a site account.
    57  type AccountSettings struct {
    58  	ReceiveAlerts bool `json:"receiveAlerts"`
    59  }
    60  
    61  // Accounts holds an array of site accounts.
    62  type Accounts = []*Account
    63  
    64  // Update copies the data of the given account to this account.
    65  func (acc *Account) Update(other *Account, setPassword bool, copyData bool) error {
    66  	if err := other.verify(false, false); err != nil {
    67  		return errors.Wrap(err, "unable to update account data")
    68  	}
    69  
    70  	// Manually update fields
    71  	acc.Title = other.Title
    72  	acc.FirstName = other.FirstName
    73  	acc.LastName = other.LastName
    74  	acc.Role = other.Role
    75  	acc.PhoneNumber = other.PhoneNumber
    76  
    77  	if setPassword && other.Password.Value != "" {
    78  		// If a password was provided, use that as the new one
    79  		if err := acc.UpdatePassword(other.Password.Value); err != nil {
    80  			return errors.Wrap(err, "unable to update account data")
    81  		}
    82  	}
    83  
    84  	if copyData {
    85  		acc.Data = other.Data
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  // Configure copies the settings of the given account to this account.
    92  func (acc *Account) Configure(other *Account) error {
    93  	// Simply copy the stored settings
    94  	acc.Settings = other.Settings
    95  
    96  	return nil
    97  }
    98  
    99  // UpdatePassword assigns a new password to the account, hashing it first.
   100  func (acc *Account) UpdatePassword(pwd string) error {
   101  	if err := acc.Password.Set(pwd); err != nil {
   102  		return errors.Wrap(err, "unable to update the user password")
   103  	}
   104  	return nil
   105  }
   106  
   107  // Clone creates a copy of the account; if erasePassword is set to true, the password will be cleared in the cloned object.
   108  func (acc *Account) Clone(erasePassword bool) *Account {
   109  	clone := *acc
   110  
   111  	if erasePassword {
   112  		clone.Password.Clear()
   113  	}
   114  
   115  	return &clone
   116  }
   117  
   118  // CheckScopeAccess checks whether the user can access the specified scope.
   119  func (acc *Account) CheckScopeAccess(scope string) bool {
   120  	hasAccess := false
   121  
   122  	switch strings.ToLower(scope) {
   123  	case ScopeDefault:
   124  		hasAccess = true
   125  
   126  	case ScopeGOCDB:
   127  		hasAccess = acc.Data.GOCDBAccess
   128  
   129  	case ScopeSite:
   130  		hasAccess = acc.Data.SiteAccess
   131  	}
   132  
   133  	return hasAccess
   134  }
   135  
   136  // Cleanup trims all string entries.
   137  func (acc *Account) Cleanup() {
   138  	acc.Email = strings.TrimSpace(acc.Email)
   139  	acc.Title = strings.TrimSpace(acc.Title)
   140  	acc.FirstName = strings.TrimSpace(acc.FirstName)
   141  	acc.LastName = strings.TrimSpace(acc.LastName)
   142  	acc.Site = strings.TrimSpace(acc.Site)
   143  	acc.Role = strings.TrimSpace(acc.Role)
   144  	acc.PhoneNumber = strings.TrimSpace(acc.PhoneNumber)
   145  }
   146  
   147  func (acc *Account) verify(isNewAccount, verifyPassword bool) error {
   148  	if acc.Email == "" {
   149  		return errors.Errorf("no email address provided")
   150  	} else if !utils.IsEmailValid(acc.Email) {
   151  		return errors.Errorf("invalid email address: %v", acc.Email)
   152  	}
   153  
   154  	if acc.FirstName == "" {
   155  		return errors.Errorf("no first name provided")
   156  	} else if !utils.IsValidName(acc.FirstName) {
   157  		return errors.Errorf("first name contains invalid characters: %v", acc.FirstName)
   158  	}
   159  
   160  	if acc.LastName == "" {
   161  		return errors.Errorf("no last name provided")
   162  	} else if !utils.IsValidName(acc.LastName) {
   163  		return errors.Errorf("last name contains invalid characters: %v", acc.LastName)
   164  	}
   165  
   166  	if isNewAccount && acc.Site == "" {
   167  		return errors.Errorf("no site provided")
   168  	}
   169  
   170  	if acc.Role == "" {
   171  		return errors.Errorf("no role provided")
   172  	} else if !utils.IsValidName(acc.Role) {
   173  		return errors.Errorf("role contains invalid characters: %v", acc.Role)
   174  	}
   175  
   176  	if acc.PhoneNumber != "" && !utils.IsValidPhoneNumber(acc.PhoneNumber) {
   177  		return errors.Errorf("invalid phone number provided")
   178  	}
   179  
   180  	if verifyPassword {
   181  		if !acc.Password.IsValid() {
   182  			return errors.Errorf("no valid password set")
   183  		}
   184  	}
   185  
   186  	return nil
   187  }
   188  
   189  // NewAccount creates a new site account.
   190  func NewAccount(email string, title, firstName, lastName string, site, role string, phoneNumber string, password string) (*Account, error) {
   191  	t := time.Now()
   192  
   193  	acc := &Account{
   194  		Email:        email,
   195  		Title:        title,
   196  		FirstName:    firstName,
   197  		LastName:     lastName,
   198  		Site:         site,
   199  		Role:         role,
   200  		PhoneNumber:  phoneNumber,
   201  		DateCreated:  t,
   202  		DateModified: t,
   203  		Data: AccountData{
   204  			GOCDBAccess: false,
   205  			SiteAccess:  false,
   206  		},
   207  		Settings: AccountSettings{
   208  			ReceiveAlerts: true,
   209  		},
   210  	}
   211  
   212  	// Set the user password, which also makes sure that the given password is strong enough
   213  	if err := acc.UpdatePassword(password); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	if err := acc.verify(true, true); err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	return acc, nil
   222  }