mirror of
https://github.com/openziti/zrok.git
synced 2025-06-19 17:27:54 +02:00
slight reworking on the configuration-based invite system to include open registration flag, contact details, and store strategy. moved to 'admin' stanza (#229)
This commit is contained in:
parent
41c30e4158
commit
b334ff50b2
@ -8,6 +8,8 @@ FEATURE: New metrics infrastructure based on OpenZiti usage events (https://gith
|
|||||||
|
|
||||||
FEATURE: New limits implementation based on the new metrics infrastructure (https://github.com/openziti/zrok/issues/235). See the [v0.4 Limits Guide](docs/guides/metrics-and-limits/configuring-limits.md) for more information.
|
FEATURE: New limits implementation based on the new metrics infrastructure (https://github.com/openziti/zrok/issues/235). See the [v0.4 Limits Guide](docs/guides/metrics-and-limits/configuring-limits.md) for more information.
|
||||||
|
|
||||||
|
FEATURE: The invite mechanism has been reworked to improve user experience. The configuration has been moved to the `admin` stanza of the controller configuration and now includes a boolean flag indicating whether or not the instance allows new invitations to be created, and also includes contact details for requesting a new invite. These values are used by the `zrok invite` command to provide a smoother end-user invite experience https://github.com/openziti/zrok/issues/229)
|
||||||
|
|
||||||
CHANGE: The controller configuration version bumps from `v: 2` to `v: 3` to support all of the new `v0.4` functionality. See the [example ctrl.yml](etc/ctrl.yml) for details on the new configuration.
|
CHANGE: The controller configuration version bumps from `v: 2` to `v: 3` to support all of the new `v0.4` functionality. See the [example ctrl.yml](etc/ctrl.yml) for details on the new configuration.
|
||||||
|
|
||||||
CHANGE: The underlying database store now utilizes a `deleted` flag on all tables to implement "soft deletes". This was necessary for the new metrics infrastructure, where we need to account for metrics data that arrived after the lifetime of a share or environment; and also we're going to need this for limits, where we need to see historical information about activity in the past (https://github.com/openziti/zrok/issues/262)
|
CHANGE: The underlying database store now utilizes a `deleted` flag on all tables to implement "soft deletes". This was necessary for the new metrics infrastructure, where we need to account for metrics data that arrived after the lifetime of a share or environment; and also we're going to need this for limits, where we need to see historical information about activity in the past (https://github.com/openziti/zrok/issues/262)
|
||||||
|
@ -62,7 +62,13 @@ func (cmd *inviteCommand) run(_ *cobra.Command, _ []string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if md != nil {
|
if md != nil {
|
||||||
cmd.tui.RequireToken(md.GetPayload().RegistrationRequiresToken)
|
if !md.GetPayload().InvitesOpen {
|
||||||
|
apiEndpoint, _ := zrd.ApiEndpoint()
|
||||||
|
tui.Error(fmt.Sprintf("'%v' is not currently accepting new users", apiEndpoint), nil)
|
||||||
|
}
|
||||||
|
cmd.tui.invitesOpen = md.GetPayload().InvitesOpen
|
||||||
|
cmd.tui.RequiresInviteToken(md.GetPayload().RequiresInviteToken)
|
||||||
|
cmd.tui.invitesContact = md.GetPayload().InviteTokenContact
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := tea.NewProgram(&cmd.tui).Run(); err != nil {
|
if _, err := tea.NewProgram(&cmd.tui).Run(); err != nil {
|
||||||
@ -97,14 +103,16 @@ func (cmd *inviteCommand) endpointError(apiEndpoint, _ string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type inviteTui struct {
|
type inviteTui struct {
|
||||||
focusIndex int
|
focusIndex int
|
||||||
msg string
|
msg string
|
||||||
emailInputs []textinput.Model
|
emailInputs []textinput.Model
|
||||||
tokenInput textinput.Model
|
tokenInput textinput.Model
|
||||||
cursorMode textinput.CursorMode
|
cursorMode textinput.CursorMode
|
||||||
done bool
|
done bool
|
||||||
requireToken bool
|
invitesOpen bool
|
||||||
maxIndex int
|
requireInviteToken bool
|
||||||
|
invitesContact string
|
||||||
|
maxIndex int
|
||||||
|
|
||||||
msgOk string
|
msgOk string
|
||||||
msgMismatch string
|
msgMismatch string
|
||||||
@ -208,7 +216,7 @@ func (m *inviteTui) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.emailInputs[i].PromptStyle = m.noStyle
|
m.emailInputs[i].PromptStyle = m.noStyle
|
||||||
m.emailInputs[i].TextStyle = m.noStyle
|
m.emailInputs[i].TextStyle = m.noStyle
|
||||||
}
|
}
|
||||||
if m.requireToken {
|
if m.requireInviteToken {
|
||||||
if m.focusIndex == 2 {
|
if m.focusIndex == 2 {
|
||||||
cmds[2] = m.tokenInput.Focus()
|
cmds[2] = m.tokenInput.Focus()
|
||||||
m.tokenInput.PromptStyle = m.focusedStyle
|
m.tokenInput.PromptStyle = m.focusedStyle
|
||||||
@ -234,7 +242,7 @@ func (m *inviteTui) updateInputs(msg tea.Msg) tea.Cmd {
|
|||||||
for i := range m.emailInputs {
|
for i := range m.emailInputs {
|
||||||
m.emailInputs[i], cmds[i] = m.emailInputs[i].Update(msg)
|
m.emailInputs[i], cmds[i] = m.emailInputs[i].Update(msg)
|
||||||
}
|
}
|
||||||
if m.requireToken {
|
if m.requireInviteToken {
|
||||||
m.tokenInput, cmds[2] = m.tokenInput.Update(msg)
|
m.tokenInput, cmds[2] = m.tokenInput.Update(msg)
|
||||||
}
|
}
|
||||||
return tea.Batch(cmds...)
|
return tea.Batch(cmds...)
|
||||||
@ -242,14 +250,19 @@ func (m *inviteTui) updateInputs(msg tea.Msg) tea.Cmd {
|
|||||||
|
|
||||||
func (m inviteTui) View() string {
|
func (m inviteTui) View() string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|
||||||
b.WriteString(fmt.Sprintf("\n%v\n\n", m.msg))
|
b.WriteString(fmt.Sprintf("\n%v\n\n", m.msg))
|
||||||
|
|
||||||
|
if m.requireInviteToken && m.invitesContact != "" {
|
||||||
|
b.WriteString(fmt.Sprintf("If you don't already have one, request an invite token at: %v\n\n", m.invitesContact))
|
||||||
|
}
|
||||||
|
|
||||||
for i := 0; i < len(m.emailInputs); i++ {
|
for i := 0; i < len(m.emailInputs); i++ {
|
||||||
b.WriteString(m.emailInputs[i].View())
|
b.WriteString(m.emailInputs[i].View())
|
||||||
b.WriteRune('\n')
|
b.WriteRune('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.requireToken {
|
if m.requireInviteToken {
|
||||||
b.WriteString(m.tokenInput.View())
|
b.WriteString(m.tokenInput.View())
|
||||||
b.WriteRune('\n')
|
b.WriteRune('\n')
|
||||||
}
|
}
|
||||||
@ -263,8 +276,8 @@ func (m inviteTui) View() string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *inviteTui) RequireToken(require bool) {
|
func (m *inviteTui) RequiresInviteToken(require bool) {
|
||||||
m.requireToken = require
|
m.requireInviteToken = require
|
||||||
if require {
|
if require {
|
||||||
m.maxIndex = 3
|
m.maxIndex = 3
|
||||||
} else {
|
} else {
|
||||||
|
@ -31,8 +31,11 @@ type Config struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AdminConfig struct {
|
type AdminConfig struct {
|
||||||
Secrets []string `cf:"+secret"`
|
Secrets []string `cf:"+secret"`
|
||||||
TouLink string
|
TouLink string
|
||||||
|
InvitesOpen bool
|
||||||
|
InviteTokenStrategy string
|
||||||
|
InviteTokenContact string
|
||||||
}
|
}
|
||||||
|
|
||||||
type EndpointConfig struct {
|
type EndpointConfig struct {
|
||||||
@ -42,7 +45,6 @@ type EndpointConfig struct {
|
|||||||
|
|
||||||
type RegistrationConfig struct {
|
type RegistrationConfig struct {
|
||||||
RegistrationUrlTemplate string
|
RegistrationUrlTemplate string
|
||||||
TokenStrategy string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordConfig struct {
|
type ResetPasswordConfig struct {
|
||||||
|
@ -19,18 +19,14 @@ func newConfigurationHandler(cfg *config.Config) *configurationHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ch *configurationHandler) Handle(_ metadata.ConfigurationParams) middleware.Responder {
|
func (ch *configurationHandler) Handle(_ metadata.ConfigurationParams) middleware.Responder {
|
||||||
tou := ""
|
|
||||||
if cfg.Admin != nil {
|
|
||||||
tou = cfg.Admin.TouLink
|
|
||||||
}
|
|
||||||
tokenRequired := false
|
|
||||||
if cfg.Registration != nil {
|
|
||||||
tokenRequired = cfg.Registration.TokenStrategy == "store"
|
|
||||||
}
|
|
||||||
data := &rest_model_zrok.Configuration{
|
data := &rest_model_zrok.Configuration{
|
||||||
Version: build.String(),
|
Version: build.String(),
|
||||||
TouLink: tou,
|
InvitesOpen: cfg.Admin != nil && cfg.Admin.InvitesOpen,
|
||||||
RegistrationRequiresToken: tokenRequired,
|
RequiresInviteToken: cfg.Registration != nil && cfg.Admin.InviteTokenStrategy == "store",
|
||||||
|
}
|
||||||
|
if cfg.Admin != nil {
|
||||||
|
data.TouLink = cfg.Admin.TouLink
|
||||||
|
data.InviteTokenContact = cfg.Admin.InviteTokenContact
|
||||||
}
|
}
|
||||||
return metadata.NewConfigurationOK().WithPayload(data)
|
return metadata.NewConfigurationOK().WithPayload(data)
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ func (h *inviteHandler) Handle(params account.InviteParams) middleware.Responder
|
|||||||
}
|
}
|
||||||
defer func() { _ = tx.Rollback() }()
|
defer func() { _ = tx.Rollback() }()
|
||||||
|
|
||||||
if h.cfg.Registration != nil && h.cfg.Registration.TokenStrategy == "store" {
|
if h.cfg.Admin != nil && h.cfg.Admin.InviteTokenStrategy == "store" {
|
||||||
inviteToken, err := str.FindInviteTokenByToken(params.Body.Token, tx)
|
inviteToken, err := str.FindInviteTokenByToken(params.Body.Token, tx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.Errorf("cannot get invite token '%v' for '%v': %v", params.Body.Token, params.Body.Email, err)
|
logrus.Errorf("cannot get invite token '%v' for '%v': %v", params.Body.Token, params.Body.Email, err)
|
||||||
|
19
etc/ctrl.yml
19
etc/ctrl.yml
@ -19,9 +19,22 @@ admin:
|
|||||||
#
|
#
|
||||||
secrets:
|
secrets:
|
||||||
- 77623cad-1847-4d6d-8ffe-37defc33c909
|
- 77623cad-1847-4d6d-8ffe-37defc33c909
|
||||||
# if `tou_link` is present, the frontend will display the "Terms of Use" link on the login and registration forms
|
#
|
||||||
|
# If `tou_link` is present, the frontend will display the "Terms of Use" link on the login and registration forms
|
||||||
#
|
#
|
||||||
tou_link: '<a href="https://google.com" target="_">Terms and Conditions</a>'
|
tou_link: '<a href="https://google.com" target="_">Terms and Conditions</a>'
|
||||||
|
#
|
||||||
|
# To allow open invites to your `zrok` instance, set `invites_open` to `true`
|
||||||
|
#
|
||||||
|
invites_open: true
|
||||||
|
#
|
||||||
|
# Set `token_strategy` to `store` to require an invite token.
|
||||||
|
#
|
||||||
|
#token_strategy: store
|
||||||
|
#
|
||||||
|
# Set `invite_token_contact` to include an email address or a URL where an invite token can be requested
|
||||||
|
#
|
||||||
|
invite_token_contact: invites@zrok.io
|
||||||
|
|
||||||
# The `bridge` section configures the `zrok controller metrics bridge`, specifying the source and sink where OpenZiti
|
# The `bridge` section configures the `zrok controller metrics bridge`, specifying the source and sink where OpenZiti
|
||||||
# `fabric.usage` events are consumed and then sent into `zrok`. For production environments, we recommend that you use
|
# `fabric.usage` events are consumed and then sent into `zrok`. For production environments, we recommend that you use
|
||||||
@ -131,10 +144,6 @@ metrics:
|
|||||||
#
|
#
|
||||||
registration:
|
registration:
|
||||||
registration_url_template: https://zrok.server.com/register
|
registration_url_template: https://zrok.server.com/register
|
||||||
#
|
|
||||||
# Set `token_strategy` to `store` to require an invite token.
|
|
||||||
#
|
|
||||||
#token_strategy: store
|
|
||||||
|
|
||||||
# Configure the generated URL for password resets. The reset token will be appended to this URL.
|
# Configure the generated URL for password resets. The reset token will be appended to this URL.
|
||||||
#
|
#
|
||||||
|
@ -17,8 +17,14 @@ import (
|
|||||||
// swagger:model configuration
|
// swagger:model configuration
|
||||||
type Configuration struct {
|
type Configuration struct {
|
||||||
|
|
||||||
// registration requires token
|
// invite token contact
|
||||||
RegistrationRequiresToken bool `json:"registrationRequiresToken,omitempty"`
|
InviteTokenContact string `json:"inviteTokenContact,omitempty"`
|
||||||
|
|
||||||
|
// invites open
|
||||||
|
InvitesOpen bool `json:"invitesOpen,omitempty"`
|
||||||
|
|
||||||
|
// requires invite token
|
||||||
|
RequiresInviteToken bool `json:"requiresInviteToken,omitempty"`
|
||||||
|
|
||||||
// tou link
|
// tou link
|
||||||
TouLink string `json:"touLink,omitempty"`
|
TouLink string `json:"touLink,omitempty"`
|
||||||
|
@ -1056,7 +1056,13 @@ func init() {
|
|||||||
"configuration": {
|
"configuration": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"registrationRequiresToken": {
|
"inviteTokenContact": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"invitesOpen": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"requiresInviteToken": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"touLink": {
|
"touLink": {
|
||||||
@ -2611,7 +2617,13 @@ func init() {
|
|||||||
"configuration": {
|
"configuration": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"registrationRequiresToken": {
|
"inviteTokenContact": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"invitesOpen": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"requiresInviteToken": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
"touLink": {
|
"touLink": {
|
||||||
|
@ -672,8 +672,12 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
touLink:
|
touLink:
|
||||||
type: string
|
type: string
|
||||||
registrationRequiresToken:
|
invitesOpen:
|
||||||
type: boolean
|
type: boolean
|
||||||
|
requiresInviteToken:
|
||||||
|
type: boolean
|
||||||
|
inviteTokenContact:
|
||||||
|
type: string
|
||||||
|
|
||||||
createFrontendRequest:
|
createFrontendRequest:
|
||||||
type: object
|
type: object
|
||||||
|
@ -31,7 +31,9 @@
|
|||||||
*
|
*
|
||||||
* @property {string} version
|
* @property {string} version
|
||||||
* @property {string} touLink
|
* @property {string} touLink
|
||||||
* @property {boolean} registrationRequiresToken
|
* @property {boolean} invitesOpen
|
||||||
|
* @property {boolean} requiresInviteToken
|
||||||
|
* @property {string} inviteTokenContact
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user