github.com/criteo/command-launcher@v0.0.0-20230407142452-fb616f546e98/cmd/consent/consent.go (about)

     1  package consent
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/criteo/command-launcher/internal/config"
     9  	"github.com/criteo/command-launcher/internal/console"
    10  	"github.com/criteo/command-launcher/internal/helper"
    11  	"github.com/spf13/viper"
    12  )
    13  
    14  type Consent struct {
    15  	ExpiresAt int64    `json:"expiresAt"`
    16  	Consents  []string `json:"consents"`
    17  }
    18  
    19  const (
    20  	USERNAME    = "USERNAME"
    21  	PASSWORD    = "PASSWORD"
    22  	LOG_LEVEL   = "LOG_LEVEL"
    23  	AUTH_TOKEN  = "AUTH_TOKEN"
    24  	DEBUG_FLAGS = "DEBUG_FLAGS"
    25  )
    26  
    27  var AvailableConsents = []string{
    28  	USERNAME, PASSWORD, AUTH_TOKEN, LOG_LEVEL, DEBUG_FLAGS,
    29  }
    30  
    31  // GetConsents function returns the user consent of a particular command
    32  // This function returns a list of agreed consents.
    33  func GetConsents(cmdGroup string, cmdName string, requests []string, enabled bool) ([]string, error) {
    34  	if !enabled {
    35  		return AvailableConsents, nil
    36  	}
    37  
    38  	if len(requests) == 0 {
    39  		return requests, nil
    40  	}
    41  
    42  	consent, err := getCmdConsents(cmdGroup, cmdName)
    43  	if err == nil {
    44  		return consent.Consents, nil
    45  	}
    46  
    47  	if requestConsent(cmdGroup, cmdName, requests) {
    48  		// user explicitly consented
    49  		keyLife := viper.GetDuration(config.USER_CONSENT_LIFE_KEY).Seconds()
    50  		if keyLife <= 0 {
    51  			// default value: expire in seconds for 30 days = 3600 * 24 * 30 = 2592000
    52  			keyLife = 2592000
    53  		}
    54  		if err := saveCmdConsents(cmdGroup, cmdName, requests, int64(keyLife)); err != nil {
    55  			return requests, err
    56  		}
    57  		return requests, nil
    58  	} // user refuse consent
    59  
    60  	return []string{}, nil
    61  }
    62  
    63  func getCmdConsents(cmdGroup string, cmdName string) (*Consent, error) {
    64  	var err error
    65  	secretKey := getConsentKey(cmdGroup, cmdName)
    66  	secretValue := ""
    67  	consent := Consent{}
    68  	if secretValue, err = helper.GetSecret(secretKey); err != nil {
    69  		return nil, err
    70  	}
    71  	if err := json.Unmarshal([]byte(secretValue), &consent); err != nil {
    72  		return nil, err
    73  	}
    74  	if time.Unix(consent.ExpiresAt, 0).Before(time.Now()) {
    75  		// expired
    76  		return nil, fmt.Errorf("consent expired")
    77  	}
    78  	return &consent, nil
    79  }
    80  
    81  func getConsentKey(cmdGroup string, cmdName string) string {
    82  	return fmt.Sprintf("%s_%s", cmdGroup, cmdName)
    83  }
    84  
    85  func saveCmdConsents(cmdGroup string, cmdName string, requests []string, duration int64) error {
    86  	secretKey := getConsentKey(cmdGroup, cmdName)
    87  	secretValue, err := json.Marshal(Consent{
    88  		ExpiresAt: time.Now().Unix() + duration,
    89  		Consents:  requests,
    90  	})
    91  	if err != nil {
    92  		return err
    93  	}
    94  	if err := helper.SetSecret(secretKey, string(secretValue)); err != nil {
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  func requestConsent(cmdGroup string, cmdName string, requests []string) bool {
   101  	fmt.Printf("Command '%s %s' requests access to the following resources:\n", cmdGroup, cmdName)
   102  	for _, request := range requests {
   103  		fmt.Printf("  - %s\n", request)
   104  	}
   105  	fmt.Println()
   106  	console.Reminder("authorize the access? [yN]")
   107  	var resp int
   108  	if _, err := fmt.Scanf("%c", &resp); err != nil || (resp != 'y' && resp != 'Y') {
   109  		fmt.Printf("Authorization refused by user\n")
   110  		fmt.Printf("-----------------------------\n\n")
   111  		return false
   112  	}
   113  	return true
   114  }