mirror of
https://github.com/greenpau/caddy-security.git
synced 2025-04-18 08:04:02 +03:00
create user registration configuration section
This commit is contained in:
parent
059a464f90
commit
a0004fe719
4
Makefile
4
Makefile
@ -16,8 +16,8 @@ all: info
|
||||
@mkdir -p ../xcaddy-$(PLUGIN_NAME) && cd ../xcaddy-$(PLUGIN_NAME) && \
|
||||
xcaddy build $(CADDY_VERSION) --output ../$(PLUGIN_NAME)/bin/caddy \
|
||||
--with github.com/greenpau/caddy-security@$(LATEST_GIT_COMMIT)=$(BUILD_DIR) \
|
||||
--with github.com/greenpau/caddy-trace@v1.1.8
|
||||
@#--with github.com/greenpau/go-authcrunch@v1.0.22=/home/greenpau/dev/go/src/github.com/greenpau/go-authcrunch
|
||||
--with github.com/greenpau/caddy-trace@v1.1.8 \
|
||||
--with github.com/greenpau/go-authcrunch@v1.0.22=/home/greenpau/dev/go/src/github.com/greenpau/go-authcrunch
|
||||
@#bin/caddy run -config assets/config/Caddyfile
|
||||
@for f in `find ./assets -type f -name 'Caddyfile'`; do bin/caddy fmt -overwrite $$f; done
|
||||
|
||||
|
@ -23,6 +23,17 @@
|
||||
path assets/config/users.json
|
||||
}
|
||||
|
||||
user registration localdbRegistry {
|
||||
dropbox assets/config/registrations.json
|
||||
title "User Registration"
|
||||
code "NY2020"
|
||||
require accept terms
|
||||
require domain mx
|
||||
email provider localhost-smtp-server
|
||||
admin email admin@localhost
|
||||
identity store localdb
|
||||
}
|
||||
|
||||
authentication portal myportal {
|
||||
crypto default token lifetime 3600
|
||||
crypto key sign-verify 01ee2688-36e4-47f9-8c06-d18483702520
|
||||
@ -38,15 +49,6 @@
|
||||
action add role authp/user
|
||||
ui link "Portal Settings" /auth/settings icon "las la-cog"
|
||||
}
|
||||
registration {
|
||||
dropbox assets/config/registrations.json
|
||||
title "User Registration"
|
||||
code "NY2020"
|
||||
require accept terms
|
||||
require domain mx
|
||||
email provider localhost-smtp-server
|
||||
admin email root@localhost
|
||||
}
|
||||
}
|
||||
|
||||
authorization policy mypolicy {
|
||||
|
@ -66,6 +66,10 @@ func parseCaddyfile(d *caddyfile.Dispenser, _ interface{}) (interface{}, error)
|
||||
if err := parseCaddyfileIdentity(d, repl, app.Config, tld); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "user":
|
||||
if err := parseCaddyfileUser(d, repl, app.Config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case "authentication":
|
||||
if err := parseCaddyfileAuthentication(d, repl, app.Config); err != nil {
|
||||
return nil, err
|
||||
|
@ -22,7 +22,6 @@ import (
|
||||
"github.com/greenpau/go-authcrunch"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authn"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authn/cookie"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authn/registration"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authn/ui"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authz/options"
|
||||
"github.com/greenpau/go-authcrunch/pkg/errors"
|
||||
@ -58,22 +57,13 @@ const (
|
||||
// cookie samesite <lax|strict|none>
|
||||
// cookie insecure <on|off>
|
||||
//
|
||||
// registration {
|
||||
// disabled <on|off>
|
||||
// title "User Registration"
|
||||
// code "NY2020"
|
||||
// dropbox <file/path/to/registration/dir/>
|
||||
// require accept terms
|
||||
// require domain mx
|
||||
// admin email <email_address> [<email_address_N>]
|
||||
// }
|
||||
//
|
||||
// validate source address
|
||||
//
|
||||
// enable source ip tracking
|
||||
// enable admin api
|
||||
// enable identity store <name>
|
||||
// enable identity provider <name>
|
||||
// enable user registration <name>
|
||||
// }
|
||||
//
|
||||
func parseCaddyfileAuthentication(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *authcrunch.Config) error {
|
||||
@ -91,10 +81,9 @@ func parseCaddyfileAuthentication(d *caddyfile.Dispenser, repl *caddy.Replacer,
|
||||
UI: &ui.Parameters{
|
||||
Templates: make(map[string]string),
|
||||
},
|
||||
UserRegistrationConfig: ®istration.Config{},
|
||||
CookieConfig: &cookie.Config{},
|
||||
TokenValidatorOptions: &options.TokenValidatorOptions{},
|
||||
TokenGrantorOptions: &options.TokenGrantorOptions{},
|
||||
CookieConfig: &cookie.Config{},
|
||||
TokenValidatorOptions: &options.TokenValidatorOptions{},
|
||||
TokenGrantorOptions: &options.TokenGrantorOptions{},
|
||||
}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
k := d.Val()
|
||||
@ -119,10 +108,6 @@ func parseCaddyfileAuthentication(d *caddyfile.Dispenser, repl *caddy.Replacer,
|
||||
if err := parseCaddyfileAuthPortalTransform(d, repl, p, rootDirective, v); err != nil {
|
||||
return err
|
||||
}
|
||||
case "registration":
|
||||
if err := parseCaddyfileAuthPortalRegistration(d, repl, p, rootDirective); err != nil {
|
||||
return err
|
||||
}
|
||||
case "enable", "validate":
|
||||
if err := parseCaddyfileAuthPortalMisc(d, repl, p, rootDirective, k, v); err != nil {
|
||||
return err
|
||||
|
@ -1,88 +0,0 @@
|
||||
// Copyright 2022 Paul Greenberg greenpau@outlook.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/greenpau/caddy-security/pkg/util"
|
||||
"github.com/greenpau/go-authcrunch/pkg/authn"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func parseCaddyfileAuthPortalRegistration(h *caddyfile.Dispenser, repl *caddy.Replacer, portal *authn.PortalConfig, rootDirective string) error {
|
||||
for nesting := h.Nesting(); h.NextBlock(nesting); {
|
||||
subDirective := h.Val()
|
||||
switch subDirective {
|
||||
case "title":
|
||||
if !h.NextArg() {
|
||||
return h.Errf("%s %s subdirective has no value", rootDirective, subDirective)
|
||||
}
|
||||
portal.UserRegistrationConfig.Title = util.FindReplace(repl, h.Val())
|
||||
case "disabled":
|
||||
if !h.NextArg() {
|
||||
return h.Errf("%s %s subdirective has no value", rootDirective, subDirective)
|
||||
}
|
||||
if h.Val() == "yes" || h.Val() == "on" {
|
||||
portal.UserRegistrationConfig.Disabled = true
|
||||
}
|
||||
case "code":
|
||||
if !h.NextArg() {
|
||||
return h.Errf("%s %s subdirective has no value", rootDirective, subDirective)
|
||||
}
|
||||
portal.UserRegistrationConfig.Code = util.FindReplace(repl, h.Val())
|
||||
case "dropbox":
|
||||
if !h.NextArg() {
|
||||
return h.Errf("%s %s subdirective has no value", rootDirective, subDirective)
|
||||
}
|
||||
portal.UserRegistrationConfig.Dropbox = util.FindReplace(repl, h.Val())
|
||||
case "require":
|
||||
args := strings.Join(h.RemainingArgs(), " ")
|
||||
args = strings.TrimSpace(args)
|
||||
switch args {
|
||||
case "accept terms":
|
||||
portal.UserRegistrationConfig.RequireAcceptTerms = true
|
||||
case "domain mx":
|
||||
portal.UserRegistrationConfig.RequireDomainMailRecord = true
|
||||
case "":
|
||||
return h.Errf("%s directive has no value", rootDirective)
|
||||
default:
|
||||
return h.Errf("%s directive %q is unsupported", rootDirective, args)
|
||||
}
|
||||
case "email":
|
||||
args := util.FindReplaceAll(repl, h.RemainingArgs())
|
||||
if len(args) != 2 {
|
||||
return h.Errf("%s directive %q invalid number of args", rootDirective, args)
|
||||
}
|
||||
if args[0] != "provider" {
|
||||
return h.Errf("%s directive must be followed by provider keyword", rootDirective)
|
||||
}
|
||||
portal.UserRegistrationConfig.EmailProvider = args[1]
|
||||
case "admin":
|
||||
args := util.FindReplaceAll(repl, h.RemainingArgs())
|
||||
if len(args) < 2 {
|
||||
return h.Errf("%s directive %q invalid number of args", rootDirective, args)
|
||||
}
|
||||
if args[0] != "email" {
|
||||
return h.Errf("%s directive must be followed by email keyword", rootDirective)
|
||||
}
|
||||
portal.UserRegistrationConfig.AdminEmails = args[1:]
|
||||
default:
|
||||
return h.Errf("unsupported subdirective for %s: %s", rootDirective, subDirective)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -51,13 +51,6 @@ func TestParseCaddyfileAuthentication(t *testing.T) {
|
||||
match origin local
|
||||
action add role authp/user
|
||||
ui link "Portal Settings" /auth/settings icon "las la-cog"
|
||||
}
|
||||
registration {
|
||||
title "User Registration"
|
||||
code "NY2020"
|
||||
dropbox assets/config/registrations.json
|
||||
require accept terms
|
||||
require domain mx
|
||||
}
|
||||
enable source ip tracking
|
||||
validate source address
|
||||
@ -158,13 +151,6 @@ func TestParseCaddyfileAuthentication(t *testing.T) {
|
||||
}
|
||||
]
|
||||
},
|
||||
"user_registration_config": {
|
||||
"title": "User Registration",
|
||||
"code": "NY2020",
|
||||
"dropbox": "assets/config/registrations.json",
|
||||
"require_accept_terms": true,
|
||||
"require_domain_mx": true
|
||||
},
|
||||
"user_transformer_configs": [
|
||||
{
|
||||
"matchers": [
|
||||
|
@ -57,7 +57,6 @@ func TestParseCaddyfileIdentityProvider(t *testing.T) {
|
||||
{
|
||||
"name": "myportal",
|
||||
"ui": {},
|
||||
"user_registration_config": {},
|
||||
"cookie_config": {},
|
||||
"identity_providers": [
|
||||
"authp"
|
||||
|
@ -47,7 +47,6 @@ func TestParseCaddyfileIdentityStore(t *testing.T) {
|
||||
{
|
||||
"name": "myportal",
|
||||
"ui": {},
|
||||
"user_registration_config": {},
|
||||
"cookie_config": {},
|
||||
"identity_stores": [
|
||||
"localdb"
|
||||
@ -105,7 +104,6 @@ func TestParseCaddyfileIdentityStore(t *testing.T) {
|
||||
{
|
||||
"name": "myportal",
|
||||
"ui": {},
|
||||
"user_registration_config": {},
|
||||
"cookie_config": {},
|
||||
"identity_stores": [
|
||||
"contoso.com"
|
||||
|
@ -68,7 +68,6 @@ func TestParseCaddyfileIdentity(t *testing.T) {
|
||||
{
|
||||
"name": "myportal",
|
||||
"ui": {},
|
||||
"user_registration_config": {},
|
||||
"cookie_config": {},
|
||||
"identity_stores": [
|
||||
"localdb"
|
||||
|
@ -44,6 +44,10 @@ const (
|
||||
// bcc <email_address_1> <email_address2>
|
||||
// }
|
||||
//
|
||||
// messaging file provider <name> {
|
||||
// rootdir <path>
|
||||
// }
|
||||
//
|
||||
func parseCaddyfileMessaging(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *authcrunch.Config) error {
|
||||
args := util.FindReplaceAll(repl, d.RemainingArgs())
|
||||
if len(args) != 3 {
|
||||
@ -55,7 +59,9 @@ func parseCaddyfileMessaging(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *
|
||||
|
||||
switch args[0] {
|
||||
case "email":
|
||||
c := &messaging.EmailProvider{Name: args[2]}
|
||||
c := &messaging.EmailProvider{
|
||||
Name: args[2],
|
||||
}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
k := d.Val()
|
||||
v := util.FindReplaceAll(repl, d.RemainingArgs())
|
||||
@ -92,6 +98,26 @@ func parseCaddyfileMessaging(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *
|
||||
if err := cfg.AddMessagingProvider(c); err != nil {
|
||||
return errors.ErrMalformedDirective.WithArgs([]string{msgPrefix, args[0], args[1]}, err)
|
||||
}
|
||||
case "file":
|
||||
p := &messaging.FileProvider{
|
||||
Name: args[2],
|
||||
}
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
k := d.Val()
|
||||
v := util.FindReplaceAll(repl, d.RemainingArgs())
|
||||
if len(v) != 1 {
|
||||
return errors.ErrMalformedDirective.WithArgs([]string{msgPrefix, args[0], k}, v)
|
||||
}
|
||||
switch k {
|
||||
case "rootdir":
|
||||
p.RootDir = v[0]
|
||||
default:
|
||||
return errors.ErrMalformedDirective.WithArgs([]string{msgPrefix, args[0], k}, v)
|
||||
}
|
||||
}
|
||||
if err := cfg.AddMessagingProvider(p); err != nil {
|
||||
return errors.ErrMalformedDirective.WithArgs([]string{msgPrefix, args[0], args[1]}, err)
|
||||
}
|
||||
default:
|
||||
return errors.ErrMalformedDirective.WithArgs(msgPrefix, args)
|
||||
}
|
||||
|
43
caddyfile_user.go
Normal file
43
caddyfile_user.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2022 Paul Greenberg greenpau@outlook.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/greenpau/caddy-security/pkg/util"
|
||||
"github.com/greenpau/go-authcrunch"
|
||||
"github.com/greenpau/go-authcrunch/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
userPrefix = "security.user"
|
||||
)
|
||||
|
||||
func parseCaddyfileUser(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *authcrunch.Config) error {
|
||||
args := util.FindReplaceAll(repl, d.RemainingArgs())
|
||||
if len(args) < 2 {
|
||||
return d.ArgErr()
|
||||
}
|
||||
switch {
|
||||
case args[0] == "registration":
|
||||
if err := parseCaddyfileUserRegistration(d, repl, cfg, args[1]); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return errors.ErrMalformedDirective.WithArgs(userPrefix, args)
|
||||
}
|
||||
return nil
|
||||
}
|
138
caddyfile_user_registration.go
Normal file
138
caddyfile_user_registration.go
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2022 Paul Greenberg greenpau@outlook.com
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package security
|
||||
|
||||
import (
|
||||
"github.com/caddyserver/caddy/v2"
|
||||
"github.com/caddyserver/caddy/v2/caddyconfig/caddyfile"
|
||||
"github.com/greenpau/caddy-security/pkg/util"
|
||||
"github.com/greenpau/go-authcrunch"
|
||||
"github.com/greenpau/go-authcrunch/pkg/errors"
|
||||
"github.com/greenpau/go-authcrunch/pkg/registry"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parseCaddyfileIdentityProvider parses identity provider configuration.
|
||||
//
|
||||
// Syntax:
|
||||
//
|
||||
// user registration <name> {
|
||||
// title <name>
|
||||
// code <name>
|
||||
// dropbox <path>
|
||||
// require accept terms
|
||||
// require domain mx
|
||||
// email provider <name>
|
||||
// admin email <email_address_1> <<email_address_N>
|
||||
// identity store <name>
|
||||
// link terms <url>
|
||||
// link privacy <url>
|
||||
// }
|
||||
//
|
||||
func parseCaddyfileUserRegistration(d *caddyfile.Dispenser, repl *caddy.Replacer, cfg *authcrunch.Config, name string) error {
|
||||
var disabled bool
|
||||
|
||||
r := ®istry.UserRegistryConfig{
|
||||
Name: name,
|
||||
}
|
||||
|
||||
for nesting := d.Nesting(); d.NextBlock(nesting); {
|
||||
k := d.Val()
|
||||
args := util.FindReplaceAll(repl, d.RemainingArgs())
|
||||
rd := mkcp("security.user.registration["+name+"]", k)
|
||||
switch k {
|
||||
case "disabled":
|
||||
disabled = true
|
||||
case "title":
|
||||
if len(args) != 1 {
|
||||
return d.Errf("%s directive %q must contain single value", rd, args)
|
||||
}
|
||||
r.Title = args[0]
|
||||
case "code":
|
||||
if len(args) != 1 {
|
||||
return d.Errf("%s directive %q must contain single value", rd, args)
|
||||
}
|
||||
r.Code = args[0]
|
||||
case "dropbox":
|
||||
if len(args) != 1 {
|
||||
return d.Errf("%s directive %q must contain single value", rd, args)
|
||||
}
|
||||
r.Dropbox = args[0]
|
||||
case "require":
|
||||
switch strings.Join(args, " ") {
|
||||
case "accept terms":
|
||||
r.RequireAcceptTerms = true
|
||||
case "domain mx":
|
||||
r.RequireDomainMailRecord = true
|
||||
case "":
|
||||
return d.Errf("%s directive has no value", rd)
|
||||
default:
|
||||
return d.Errf("%s directive %q is unsupported", rd, args)
|
||||
}
|
||||
case "link":
|
||||
if len(args) != 2 {
|
||||
return d.Errf("%s directive %q must contain key-value pair", rd, args)
|
||||
}
|
||||
switch args[0] {
|
||||
case "terms":
|
||||
r.TermsConditionsLink = args[1]
|
||||
case "privacy":
|
||||
r.PrivacyPolicyLink = args[1]
|
||||
default:
|
||||
return d.Errf("%s directive %q contains unsupported value", rd, args)
|
||||
}
|
||||
case "email":
|
||||
if len(args) != 2 {
|
||||
return d.Errf("%s directive %q must contain key-value pair", rd, args)
|
||||
}
|
||||
switch args[0] {
|
||||
case "provider":
|
||||
r.EmailProvider = args[1]
|
||||
default:
|
||||
return d.Errf("%s directive %q contains unsupported value", rd, args)
|
||||
}
|
||||
case "identity":
|
||||
if len(args) != 2 {
|
||||
return d.Errf("%s directive %q must contain key-value pair", rd, args)
|
||||
}
|
||||
switch args[0] {
|
||||
case "store":
|
||||
r.IdentityStore = args[1]
|
||||
default:
|
||||
return d.Errf("%s directive %q contains unsupported value", rd, args)
|
||||
}
|
||||
case "admin":
|
||||
if len(args) < 2 {
|
||||
return d.Errf("%s directive %q must contain key-value pair", rd, args)
|
||||
}
|
||||
switch args[0] {
|
||||
case "email", "emails":
|
||||
r.AdminEmails = args[1:]
|
||||
default:
|
||||
return d.Errf("%s directive %q contains unsupported value", rd, args)
|
||||
}
|
||||
default:
|
||||
return errors.ErrMalformedDirective.WithArgs(rd, args)
|
||||
}
|
||||
}
|
||||
|
||||
if !disabled {
|
||||
if err := cfg.AddUserRegistry(r); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user