|
| 1 | +package auth |
| 2 | + |
| 3 | +import ( |
| 4 | + "bufio" |
| 5 | + "bytes" |
| 6 | + "encoding/json" |
| 7 | + "fmt" |
| 8 | + "os" |
| 9 | + |
| 10 | + "github.com/santhosh-tekuri/jsonschema/v5" |
| 11 | + "github.com/spf13/cobra" |
| 12 | + "golang.org/x/term" |
| 13 | +) |
| 14 | + |
| 15 | +// Returns a responder that responds to authentication requirements of type |
| 16 | +// `authn.MethodTypeAsk`. Reads the JSON Schema returned by the `ask` endpoint |
| 17 | +// and uses it to ask appropriate questions to the user on their terminal, and |
| 18 | +// then returns their response as serialized JSON. |
| 19 | +func askResponder(cmd *cobra.Command) responder { |
| 20 | + return func(request *json.RawMessage) ([]byte, error) { |
| 21 | + compiler := jsonschema.NewCompiler() |
| 22 | + compiler.ExtractAnnotations = true |
| 23 | + |
| 24 | + if err := compiler.AddResource("", bytes.NewReader(*request)); err != nil { |
| 25 | + return nil, err |
| 26 | + } |
| 27 | + |
| 28 | + schema, err := compiler.Compile("") |
| 29 | + if err != nil { |
| 30 | + return nil, err |
| 31 | + } |
| 32 | + |
| 33 | + response := make(map[string]any, len(schema.Properties)) |
| 34 | + for _, name := range schema.Required { |
| 35 | + subschema := schema.Properties[name] |
| 36 | + |
| 37 | + if len(subschema.Types) < 1 { |
| 38 | + return nil, fmt.Errorf("invalid schema: property %q has no type", name) |
| 39 | + } |
| 40 | + |
| 41 | + typ := subschema.Types[0] |
| 42 | + if typ == "object" { |
| 43 | + return nil, fmt.Errorf("invalid schema: property %q has non-scalar type", name) |
| 44 | + } |
| 45 | + |
| 46 | + fmt.Fprintf(cmd.ErrOrStderr(), "%s: ", name) |
| 47 | + |
| 48 | + var input []byte |
| 49 | + var err error |
| 50 | + |
| 51 | + // If the property is marked as write only, assume it is a sensitive |
| 52 | + // value and make sure we don't display it in the terminal |
| 53 | + if subschema.WriteOnly { |
| 54 | + input, err = term.ReadPassword(int(os.Stdin.Fd())) |
| 55 | + fmt.Fprintln(cmd.ErrOrStderr()) |
| 56 | + } else { |
| 57 | + reader := bufio.NewScanner(cmd.InOrStdin()) |
| 58 | + if reader.Scan() { |
| 59 | + input = reader.Bytes() |
| 60 | + } |
| 61 | + err = reader.Err() |
| 62 | + } |
| 63 | + |
| 64 | + if err != nil { |
| 65 | + return nil, err |
| 66 | + } |
| 67 | + response[name] = string(input) |
| 68 | + } |
| 69 | + |
| 70 | + respBytes, err := json.Marshal(response) |
| 71 | + if err != nil { |
| 72 | + return nil, err |
| 73 | + } |
| 74 | + |
| 75 | + return respBytes, schema.Validate(response) |
| 76 | + } |
| 77 | +} |
0 commit comments