github.com/pelicanplatform/pelican@v1.0.5/web_ui/ui_unix.go (about)

     1  //go:build !windows
     2  
     3  /***************************************************************
     4   *
     5   * Copyright (C) 2023, Pelican Project, Morgridge Institute for Research
     6   *
     7   * Licensed under the Apache License, Version 2.0 (the "License"); you
     8   * may not use this file except in compliance with the License.  You may
     9   * obtain a copy of the License at
    10   *
    11   *    http://www.apache.org/licenses/LICENSE-2.0
    12   *
    13   * Unless required by applicable law or agreed to in writing, software
    14   * distributed under the License is distributed on an "AS IS" BASIS,
    15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16   * See the License for the specific language governing permissions and
    17   * limitations under the License.
    18   *
    19   ***************************************************************/
    20  
    21  package web_ui
    22  
    23  import (
    24  	"bufio"
    25  	"fmt"
    26  	"os"
    27  	"path/filepath"
    28  	"strings"
    29  	"syscall"
    30  
    31  	"github.com/pelicanplatform/pelican/param"
    32  	"github.com/pkg/errors"
    33  	log "github.com/sirupsen/logrus"
    34  	"golang.org/x/crypto/bcrypt"
    35  )
    36  
    37  func doReload() error {
    38  	db := authDB.Load()
    39  	if db == nil {
    40  		log.Debug("Cannot reload auth database - not configured")
    41  		return nil
    42  	}
    43  	fileName := param.Server_UIPasswordFile.GetString()
    44  	fp, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE, 0600)
    45  	if err != nil {
    46  		log.Warning("Failed to open auth database for reload:", err)
    47  		return err
    48  	}
    49  	defer fp.Close()
    50  	if err = syscall.Flock(int(fp.Fd()), syscall.LOCK_SH); err != nil {
    51  		log.Warning("Failed to lock the auth database for read:", err)
    52  		return err
    53  	}
    54  	defer func() {
    55  		if err := syscall.Flock(int(fp.Fd()), syscall.LOCK_UN); err != nil {
    56  			log.Warning("Failed to unlock the auth database:", err)
    57  		}
    58  	}()
    59  
    60  	err = db.Reload(nil)
    61  	if err != nil {
    62  		log.Warningln("Failed to reload auth database:", err)
    63  		return err
    64  	}
    65  	log.Debug("Successfully reloaded the auth database")
    66  	return nil
    67  }
    68  
    69  func writePasswordEntryImpl(user, password string) error {
    70  	fileName := param.Server_UIPasswordFile.GetString()
    71  	passwordBytes := []byte(password)
    72  	if len(passwordBytes) > 72 {
    73  		return errors.New("Password too long")
    74  	}
    75  	hashed, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
    76  	if err != nil {
    77  		return err
    78  	}
    79  
    80  	directory := filepath.Dir(fileName)
    81  	err = os.MkdirAll(directory, 0750)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	fp, err := os.OpenFile(fileName, os.O_RDWR|os.O_CREATE, 0600)
    86  	if err != nil {
    87  		return err
    88  	}
    89  	defer fp.Close()
    90  
    91  	if _, err := fp.Seek(0, 0); err != nil {
    92  		log.Warning("Failed to seek to the beginning of the auth database:", err)
    93  		return err
    94  	}
    95  
    96  	if err = syscall.Flock(int(fp.Fd()), syscall.LOCK_EX); err != nil {
    97  		log.Warning("Failed to lock the auth database for read:", err)
    98  		return err
    99  	}
   100  	defer func() {
   101  		if err := syscall.Flock(int(fp.Fd()), syscall.LOCK_UN); err != nil {
   102  			log.Warning("Failed to unlock the auth database:", err)
   103  		}
   104  	}()
   105  
   106  	credentials := make(map[string]string)
   107  	scanner := bufio.NewScanner(fp)
   108  	scanner.Split(bufio.ScanLines)
   109  	for scanner.Scan() {
   110  		info := strings.SplitN(scanner.Text(), ":", 2)
   111  		if len(info) == 1 {
   112  			log.Warning("Invalid line in the authdb file:", scanner.Text())
   113  			continue
   114  		}
   115  		credentials[info[0]] = info[1]
   116  	}
   117  	credentials[user] = string(hashed)
   118  
   119  	fp2, err := os.OpenFile(fileName, os.O_RDWR|os.O_TRUNC|os.O_APPEND, 0600)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	defer fp2.Close()
   124  
   125  	for user, pass := range credentials {
   126  		entry := fmt.Sprintf("%s:%s\n", user, pass)
   127  		if _, err = fp2.Write([]byte(entry)); err != nil {
   128  			return err
   129  		}
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func WritePasswordEntry(user, password string) error {
   136  	if err := writePasswordEntryImpl(user, password); err != nil {
   137  		return err
   138  	}
   139  
   140  	db := authDB.Load()
   141  	if db != nil {
   142  		if err := db.Reload(nil); err != nil {
   143  			return err
   144  		}
   145  	}
   146  	return nil
   147  }