diff --git a/cmd/zrok/invite.go b/cmd/zrok/invite.go index fa0420f4..11e14e00 100644 --- a/cmd/zrok/invite.go +++ b/cmd/zrok/invite.go @@ -9,6 +9,7 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/openziti/zrok/rest_client_zrok/account" + "github.com/openziti/zrok/rest_client_zrok/metadata" "github.com/openziti/zrok/rest_model_zrok" "github.com/openziti/zrok/tui" "github.com/openziti/zrok/util" @@ -21,9 +22,8 @@ func init() { } type inviteCommand struct { - cmd *cobra.Command - token string - tui inviteTui + cmd *cobra.Command + tui inviteTui } func newInviteCommand() *inviteCommand { @@ -38,8 +38,6 @@ func newInviteCommand() *inviteCommand { } cmd.Run = command.run - cmd.Flags().StringVar(&command.token, "token", "", "Invite token required when zrok running in token store mode") - return command } @@ -58,17 +56,27 @@ func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) { panic(err) } + md, err := zrok.Metadata.Configuration(metadata.NewConfigurationParams()) + if err != nil { + tui.Error("unable to get server metadata", err) + } + + if md != nil { + cmd.tui.RequireToken(md.GetPayload().RegistrationRequiresToken) + } + if _, err := tea.NewProgram(&cmd.tui).Run(); err != nil { tui.Error("unable to run interface", err) os.Exit(1) } if cmd.tui.done { - email := cmd.tui.inputs[0].Value() + email := cmd.tui.emailInputs[0].Value() + token := cmd.tui.tokenInput.Value() req := account.NewInviteParams() req.Body = &rest_model_zrok.InviteRequest{ Email: email, - Token: cmd.token, + Token: token, } _, err = zrok.Account.Invite(req) if err != nil { @@ -83,18 +91,20 @@ func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) { func (cmd *inviteCommand) endpointError(apiEndpoint, _ string) { fmt.Printf("%v\n\n", tui.SeriousBusiness.Render("there was a problem creating an invitation!")) fmt.Printf("you are trying to use the zrok service at: %v\n\n", tui.Code.Render(apiEndpoint)) - fmt.Printf("%v\n\n", tui.Attention.Render("should you be using a --token? check with your instance administrator!")) fmt.Printf("you can change your zrok service endpoint using this command:\n\n") fmt.Printf("%v\n\n", tui.Code.Render("$ zrok config set apiEndpoint ")) fmt.Printf("(where newEndpoint is something like: %v)\n\n", tui.Code.Render("https://some.zrok.io")) } type inviteTui struct { - focusIndex int - msg string - inputs []textinput.Model - cursorMode textinput.CursorMode - done bool + focusIndex int + msg string + emailInputs []textinput.Model + tokenInput textinput.Model + cursorMode textinput.CursorMode + done bool + requireToken bool + maxIndex int msgOk string msgMismatch string @@ -110,7 +120,8 @@ type inviteTui struct { func newInviteTui() inviteTui { m := inviteTui{ - inputs: make([]textinput.Model, 2), + emailInputs: make([]textinput.Model, 2), + maxIndex: 2, } m.focusedStyle = tui.Attention.Copy() m.blurredStyle = tui.Code.Copy() @@ -125,7 +136,7 @@ func newInviteTui() inviteTui { m.msgMismatch = m.errorStyle.Render("email is invalid or does not match confirmation...") var t textinput.Model - for i := range m.inputs { + for i := range m.emailInputs { t = textinput.New() t.CursorStyle = m.cursorStyle t.CharLimit = 96 @@ -140,9 +151,13 @@ func newInviteTui() inviteTui { t.Placeholder = "Confirm Email" } - m.inputs[i] = t + m.emailInputs[i] = t } + m.tokenInput = textinput.New() + m.tokenInput.CursorStyle = m.cursorStyle + m.tokenInput.Placeholder = "Token" + return m } @@ -158,8 +173,8 @@ func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case "tab", "shift+tab", "enter", "up", "down": s := msg.String() - if s == "enter" && m.focusIndex == len(m.inputs) { - if util.IsValidEmail(m.inputs[0].Value()) && m.inputs[0].Value() == m.inputs[1].Value() { + if s == "enter" && m.focusIndex == m.maxIndex { + if util.IsValidEmail(m.emailInputs[0].Value()) && m.emailInputs[0].Value() == m.emailInputs[1].Value() { m.done = true return m, tea.Quit } @@ -175,23 +190,34 @@ func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.focusIndex++ } - if m.focusIndex > len(m.inputs) { + if m.focusIndex > m.maxIndex { m.focusIndex = 0 } else if m.focusIndex < 0 { - m.focusIndex = len(m.inputs) + m.focusIndex = m.maxIndex } - cmds := make([]tea.Cmd, len(m.inputs)) - for i := 0; i <= len(m.inputs)-1; i++ { + cmds := make([]tea.Cmd, m.maxIndex) + for i := 0; i <= len(m.emailInputs)-1; i++ { if i == m.focusIndex { - cmds[i] = m.inputs[i].Focus() - m.inputs[i].PromptStyle = m.focusedStyle - m.inputs[i].TextStyle = m.focusedStyle + cmds[i] = m.emailInputs[i].Focus() + m.emailInputs[i].PromptStyle = m.focusedStyle + m.emailInputs[i].TextStyle = m.focusedStyle continue } - m.inputs[i].Blur() - m.inputs[i].PromptStyle = m.noStyle - m.inputs[i].TextStyle = m.noStyle + m.emailInputs[i].Blur() + m.emailInputs[i].PromptStyle = m.noStyle + m.emailInputs[i].TextStyle = m.noStyle + } + if m.requireToken { + if m.focusIndex == 2 { + cmds[2] = m.tokenInput.Focus() + m.tokenInput.PromptStyle = m.focusedStyle + m.tokenInput.TextStyle = m.focusedStyle + } else { + m.tokenInput.Blur() + m.tokenInput.PromptStyle = m.noStyle + m.tokenInput.TextStyle = m.noStyle + } } return m, tea.Batch(cmds...) @@ -204,9 +230,12 @@ func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m *inviteTui) updateInputs(msg tea.Msg) tea.Cmd { - cmds := make([]tea.Cmd, len(m.inputs)) - for i := range m.inputs { - m.inputs[i], cmds[i] = m.inputs[i].Update(msg) + cmds := make([]tea.Cmd, m.maxIndex) + for i := range m.emailInputs { + m.emailInputs[i], cmds[i] = m.emailInputs[i].Update(msg) + } + if m.requireToken { + m.tokenInput, cmds[2] = m.tokenInput.Update(msg) } return tea.Batch(cmds...) } @@ -215,18 +244,30 @@ func (m inviteTui) View() string { var b strings.Builder b.WriteString(fmt.Sprintf("\n%v\n\n", m.msg)) - for i := range m.inputs { - b.WriteString(m.inputs[i].View()) - if i < len(m.inputs)-1 { - b.WriteRune('\n') - } + for i := 0; i < len(m.emailInputs); i++ { + b.WriteString(m.emailInputs[i].View()) + b.WriteRune('\n') + } + + if m.requireToken { + b.WriteString(m.tokenInput.View()) + b.WriteRune('\n') } button := &m.blurredButton - if m.focusIndex == len(m.inputs) { + if m.focusIndex == m.maxIndex { button = &m.focusedButton } _, _ = fmt.Fprintf(&b, "\n\n%s\n\n", *button) return b.String() } + +func (m *inviteTui) RequireToken(require bool) { + m.requireToken = require + if require { + m.maxIndex = 3 + } else { + m.maxIndex = 2 + } +} diff --git a/controller/configuration.go b/controller/configuration.go index 89071813..0b6d418d 100644 --- a/controller/configuration.go +++ b/controller/configuration.go @@ -22,9 +22,14 @@ func (ch *configurationHandler) Handle(_ metadata.ConfigurationParams) middlewar if cfg.Admin != nil { tou = cfg.Admin.TouLink } + tokenRequired := false + if cfg.Registration != nil { + tokenRequired = cfg.Registration.TokenStrategy == "store" + } data := &rest_model_zrok.Configuration{ - Version: build.String(), - TouLink: tou, + Version: build.String(), + TouLink: tou, + RegistrationRequiresToken: tokenRequired, } return metadata.NewConfigurationOK().WithPayload(data) } diff --git a/rest_model_zrok/configuration.go b/rest_model_zrok/configuration.go index 60d2618a..e5103c43 100644 --- a/rest_model_zrok/configuration.go +++ b/rest_model_zrok/configuration.go @@ -17,6 +17,9 @@ import ( // swagger:model configuration type Configuration struct { + // registration requires token + RegistrationRequiresToken bool `json:"registrationRequiresToken,omitempty"` + // tou link TouLink string `json:"touLink,omitempty"` diff --git a/rest_server_zrok/embedded_spec.go b/rest_server_zrok/embedded_spec.go index 78914348..4a2a500b 100644 --- a/rest_server_zrok/embedded_spec.go +++ b/rest_server_zrok/embedded_spec.go @@ -871,6 +871,9 @@ func init() { "configuration": { "type": "object", "properties": { + "registrationRequiresToken": { + "type": "boolean" + }, "touLink": { "type": "string" }, @@ -2149,6 +2152,9 @@ func init() { "configuration": { "type": "object", "properties": { + "registrationRequiresToken": { + "type": "boolean" + }, "touLink": { "type": "string" }, diff --git a/specs/zrok.yml b/specs/zrok.yml index c26a05e3..8fd906e1 100644 --- a/specs/zrok.yml +++ b/specs/zrok.yml @@ -553,6 +553,8 @@ definitions: type: string touLink: type: string + registrationRequiresToken: + type: boolean createFrontendRequest: type: object diff --git a/ui/src/api/types.js b/ui/src/api/types.js index 1aec6b55..61b5d0ba 100644 --- a/ui/src/api/types.js +++ b/ui/src/api/types.js @@ -30,6 +30,7 @@ * * @property {string} version * @property {string} touLink + * @property {boolean} registrationRequiresToken */ /**