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 }