Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add site account registration panel #1595

Merged
merged 11 commits into from
Mar 30, 2021
5 changes: 5 additions & 0 deletions changelog/unreleased/siteacc-reg-panel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Add site account registration panel

This PR adds a site account registration panel to the site accounts service. It also removes site registration from the xcloud metrics driver.

https://github.com/cs3org/reva/pull/1595
18 changes: 18 additions & 0 deletions docs/content/en/docs/config/http/services/siteacc/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ prefix = "/siteacc"
{{< /highlight >}}
{{% /dir %}}

{{% dir name="enable_registration_form" type="string" default="false" %}}
If set to true, the service will expose a simple form for account registration.

{{< highlight toml >}}
[http.services.siteacc]
enable_registration_form = true
{{< /highlight >}}
{{% /dir %}}

{{% dir name="notifications_mail" type="string" default="" %}}
An email address where all notifications are sent to.
{{< highlight toml >}}
Expand Down Expand Up @@ -93,3 +102,12 @@ The file location.
file = "/var/reva/accounts.json"
{{< /highlight >}}
{{% /dir %}}

## Site registration settings
{{% dir name="url" type="string" default="" %}}
The registration service URL.
{{< highlight toml >}}
[http.services.siteacc.sitereg]
url = "https://iop.example.com/sitereg"
{{< /highlight >}}
{{% /dir %}}
4 changes: 1 addition & 3 deletions examples/metrics/xcloud.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ address = "0.0.0.0:5550"
metrics_data_driver_type = "xcloud"
metrics_record_interval = 5000
xcloud_instance = "http://localhost"
xcloud_interval = 5
xcloud_catalog = 'https://sciencemesh-test.uni-muenster.de/api/mentix/sites?action=register'
insecure_skip_verify = true
xcloud_pull_interval = 60

[http.services.prometheus]
2 changes: 2 additions & 0 deletions examples/siteacc/siteacc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
address = "0.0.0.0:9600"

[http.services.siteacc]
# If this is set to true, the service will expose a simple form for account registration
enable_registration_form = true
# All notification emails are sent to this email
notifications_mail = "science.mesh@example.com"

Expand Down
6 changes: 6 additions & 0 deletions internal/http/services/siteacc/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ type Configuration struct {
} `mapstructure:"file"`
} `mapstructure:"storage"`

EnableRegistrationForm bool `mapstructure:"enable_registration_form"`

SMTP *smtpclient.SMTPCredentials `mapstructure:"smtp"`
NotificationsMail string `mapstructure:"notifications_mail"`

SiteRegistration struct {
URL string `mapstructure:"url"`
} `mapstructure:"sitereg"`
}
6 changes: 5 additions & 1 deletion internal/http/services/siteacc/config/endpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ package config
const (
// EndpointPanel is the endpoint path of the web interface panel.
EndpointPanel = "/panel"
// EndpointRegistration is the endpoint path of the web interface registration form.
EndpointRegistration = "/register"

// EndpointGenerateAPIKey is the endpoint path of the API key generator.
EndpointGenerateAPIKey = "/generate-api-key"
Expand All @@ -41,8 +43,10 @@ const (
// EndpointRemove is the endpoint path for account removal.
EndpointRemove = "/remove"

// EndpointAuthorize is the endpoint path for account authorization
// EndpointAuthorize is the endpoint path for account authorization.
EndpointAuthorize = "/authorize"
// EndpointIsAuthorized is the endpoint path used to check the authorization status of an account.
EndpointIsAuthorized = "/is-authorized"
// EndpointUnregisterSite is the endpoint path for site unregistration.
EndpointUnregisterSite = "/unregister-site"
)
3 changes: 1 addition & 2 deletions internal/http/services/siteacc/data/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package data

import (
"strings"
"time"

"github.com/pkg/errors"
Expand Down Expand Up @@ -51,7 +50,7 @@ type Accounts = []*Account

// GetSiteID returns the site ID (generated from the API key) for the given account.
func (acc *Account) GetSiteID() key.SiteIdentifier {
if id, err := key.CalculateSiteID(acc.Data.APIKey, strings.ToLower(acc.Email)); err == nil {
if id, err := key.CalculateSiteID(acc.Data.APIKey, key.SaltFromEmail(acc.Email)); err == nil {
return id
}

Expand Down
44 changes: 41 additions & 3 deletions internal/http/services/siteacc/manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import (
"github.com/cs3org/reva/internal/http/services/siteacc/data"
"github.com/cs3org/reva/internal/http/services/siteacc/email"
"github.com/cs3org/reva/internal/http/services/siteacc/panel"
"github.com/cs3org/reva/internal/http/services/siteacc/registration"
"github.com/cs3org/reva/internal/http/services/siteacc/sitereg"
"github.com/cs3org/reva/pkg/mentix/key"
"github.com/cs3org/reva/pkg/smtpclient"
)
Expand All @@ -54,8 +56,9 @@ type Manager struct {
accounts data.Accounts
storage data.Storage

panel *panel.Panel
smtp *smtpclient.SMTPCredentials
panel *panel.Panel
registrationForm *registration.Form
smtp *smtpclient.SMTPCredentials

mutex sync.RWMutex
}
Expand Down Expand Up @@ -88,6 +91,13 @@ func (mngr *Manager) initialize(conf *config.Configuration, log *zerolog.Logger)
return errors.Wrap(err, "unable to create panel")
}

// Create the web interface registrationForm
if frm, err := registration.NewForm(conf, log); err == nil {
mngr.registrationForm = frm
} else {
return errors.Wrap(err, "unable to create registrationForm")
}

// Create the SMTP client
if conf.SMTP != nil {
mngr.smtp = smtpclient.NewSMTPCredentials(conf.SMTP)
Expand Down Expand Up @@ -163,6 +173,11 @@ func (mngr *Manager) ShowPanel(w http.ResponseWriter) error {
return mngr.panel.Execute(w, &accounts)
}

// ShowRegistrationForm writes the registration registrationForm HTTP output directly to the response writer.
func (mngr *Manager) ShowRegistrationForm(w http.ResponseWriter) error {
return mngr.registrationForm.Execute(w)
}

// CreateAccount creates a new account; if an account with the same email address already exists, an error is returned.
func (mngr *Manager) CreateAccount(accountData *data.Account) error {
mngr.mutex.Lock()
Expand Down Expand Up @@ -261,7 +276,7 @@ func (mngr *Manager) AssignAPIKeyToAccount(accountData *data.Account, flags int)
}

for {
apiKey, err := key.GenerateAPIKey(strings.ToLower(account.Email), flags) // Use the (lowered) email address as the key's salt value
apiKey, err := key.GenerateAPIKey(key.SaltFromEmail(account.Email), flags)
if err != nil {
return errors.Wrap(err, "error while generating API key")
}
Expand All @@ -283,6 +298,29 @@ func (mngr *Manager) AssignAPIKeyToAccount(accountData *data.Account, flags int)
return nil
}

// UnregisterAccountSite unregisters the site associated with the given account.
func (mngr *Manager) UnregisterAccountSite(accountData *data.Account) error {
mngr.mutex.RLock()
defer mngr.mutex.RUnlock()

account, err := mngr.findAccount(FindByEmail, accountData.Email)
if err != nil {
return errors.Wrap(err, "no account with the specified email exists")
}

salt := key.SaltFromEmail(account.Email)
siteID, err := key.CalculateSiteID(account.Data.APIKey, salt)
if err != nil {
return errors.Wrap(err, "unable to get site ID")
}

if err := sitereg.UnregisterSite(mngr.conf.SiteRegistration.URL, account.Data.APIKey, siteID, salt); err != nil {
return errors.Wrap(err, "error while unregistering the site")
}

return nil
}

// RemoveAccount removes the account identified by the account email; if no such account exists, an error is returned.
func (mngr *Manager) RemoveAccount(accountData *data.Account) error {
mngr.mutex.Lock()
Expand Down
7 changes: 5 additions & 2 deletions internal/http/services/siteacc/panel/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,14 @@ const panelTemplate = `
<button type="button" onClick="handleAction('assign-api-key?isScienceMesh', '{{.Email}}');" {{if .Data.APIKey}}disabled{{end}}>ScienceMesh API Key</button>
{{if .Data.Authorized}}
<button type="button" onClick="handleAction('authorize?status=false', '{{.Email}}');">Unauthorize</button>
<button type="button" onClick="handleAction('authorize?status=false', '{{.Email}}');" {{if not .Data.APIKey}}disabled{{end}}>Unauthorize</button>
{{else}}
<button type="button" onClick="handleAction('authorize?status=true', '{{.Email}}');">Authorize</button>
<button type="button" onClick="handleAction('authorize?status=true', '{{.Email}}');" {{if not .Data.APIKey}}disabled{{end}}>Authorize</button>
{{end}}
<span style="width: 25px;">&nbsp;</span>
<button type="button" onClick="handleAction('unregister-site', '{{.Email}}');" {{if not .Data.APIKey}}disabled{{end}}>Unregister site</button>
<span style="width: 25px;">&nbsp;</span>
<button type="button" onClick="handleAction('remove', '{{.Email}}');">Remove</button>
</form>
Expand Down
76 changes: 76 additions & 0 deletions internal/http/services/siteacc/registration/form.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2018-2020 CERN
//
// 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.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package registration

import (
"html/template"
"net/http"

"github.com/pkg/errors"
"github.com/rs/zerolog"

"github.com/cs3org/reva/internal/http/services/siteacc/config"
)

// Form represents the web interface form for user account registration.
type Form struct {
conf *config.Configuration
log *zerolog.Logger

tpl *template.Template
}

func (form *Form) initialize(conf *config.Configuration, log *zerolog.Logger) error {
if conf == nil {
return errors.Errorf("no configuration provided")
}
form.conf = conf

if log == nil {
return errors.Errorf("no logger provided")
}
form.log = log

// Create the form template
form.tpl = template.New("form")
if _, err := form.tpl.Parse(formTemplate); err != nil {
return errors.Wrap(err, "error while parsing form template")
}

return nil
}

// Execute generates the HTTP output of the form and writes it to the response writer.
func (form *Form) Execute(w http.ResponseWriter) error {
type TemplateData struct {
}

tplData := TemplateData{}

return form.tpl.Execute(w, tplData)
}

// NewForm creates a new web interface form.
func NewForm(conf *config.Configuration, log *zerolog.Logger) (*Form, error) {
form := &Form{}
if err := form.initialize(conf, log); err != nil {
return nil, errors.Wrapf(err, "unable to initialize the form")
}
return form, nil
}
Loading