Skip to content

Commit

Permalink
'machine' authentication to impersonate users through a shared secret (
Browse files Browse the repository at this point in the history
  • Loading branch information
gmgigi96 authored Sep 1, 2021
1 parent 447121c commit eec4c6c
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 11 deletions.
7 changes: 7 additions & 0 deletions changelog/unreleased/machine-auth.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Enhancement: Machine auth provider

Adds a new authentication method used to impersonate users,
using a shared secret, called api-key.


https://github.com/cs3org/reva/pull/2028
38 changes: 27 additions & 11 deletions cmd/reva/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,28 @@ import (
"bufio"
"context"
"encoding/gob"
"errors"
"fmt"
"io"
"os"

registry "github.com/cs3org/go-cs3apis/cs3/auth/registry/v1beta1"
gateway "github.com/cs3org/go-cs3apis/cs3/gateway/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/pkg/errors"
)

var loginCommand = func() *command {
cmd := newCommand("login")
cmd.Description = func() string { return "login into the reva server" }
cmd.Usage = func() string { return "Usage: login <type>" }
listFlag := cmd.Bool("list", false, "list available login methods")
usernameOpt := cmd.String("username", "", "provide the username (only with machine auth)")
apiKeyOpt := cmd.String("api-key", "", "secret for the machine auth")

cmd.ResetFlags = func() {
*listFlag = false
*usernameOpt = ""
*apiKeyOpt = ""
}

cmd.Action = func(w ...io.Writer) error {
Expand Down Expand Up @@ -81,17 +85,29 @@ var loginCommand = func() *command {
}

authType := cmd.Args()[0]
reader := bufio.NewReader(os.Stdin)
fmt.Print("username: ")
username, err := read(reader)
if err != nil {
return err
}
var username, password string
var err error

// if the user select the machine authentication, the only way
// to provide the username and the password (api-key) is through
// the flags -username and -api-key respectively
if authType == "machine" {
username = *usernameOpt
password = *apiKeyOpt
} else {
// for the other methods, take the username and pw from the stdin
reader := bufio.NewReader(os.Stdin)
fmt.Print("username: ")
username, err = read(reader)
if err != nil {
return err
}

fmt.Print("password: ")
password, err := readPassword(0)
if err != nil {
return err
fmt.Print("password: ")
password, err = readPassword(0)
if err != nil {
return err
}
}

client, err := getClient()
Expand Down
1 change: 1 addition & 0 deletions pkg/auth/manager/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
_ "github.com/cs3org/reva/pkg/auth/manager/impersonator"
_ "github.com/cs3org/reva/pkg/auth/manager/json"
_ "github.com/cs3org/reva/pkg/auth/manager/ldap"
_ "github.com/cs3org/reva/pkg/auth/manager/machine"
_ "github.com/cs3org/reva/pkg/auth/manager/oidc"
_ "github.com/cs3org/reva/pkg/auth/manager/publicshares"
// Add your own here
Expand Down
100 changes: 100 additions & 0 deletions pkg/auth/manager/machine/machine.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Copyright 2018-2021 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 machine

import (
"context"

authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
"github.com/cs3org/reva/pkg/auth"
"github.com/cs3org/reva/pkg/auth/manager/registry"
"github.com/cs3org/reva/pkg/auth/scope"
"github.com/cs3org/reva/pkg/errtypes"
"github.com/cs3org/reva/pkg/rgrpc/todo/pool"
"github.com/mitchellh/mapstructure"
"github.com/pkg/errors"
)

// 'machine' is an authentication method used to impersonate users.
// To impersonate the given user it's only needed an api-key, saved
// in a config file.

type manager struct {
APIKey string `mapstructure:"api_key"`
GatewayAddr string `mapstructure:"gateway_addr"`
}

func init() {
registry.Register("machine", New)
}

// Configure parses the map conf
func (m *manager) Configure(conf map[string]interface{}) error {
err := mapstructure.Decode(conf, m)
if err != nil {
return errors.Wrap(err, "error decoding conf")
}
return nil
}

// New creates a new manager for the 'machine' authentication
func New(conf map[string]interface{}) (auth.Manager, error) {
m := &manager{}
err := m.Configure(conf)
if err != nil {
return nil, err
}
return m, nil
}

// Authenticate impersonate an user if the provided secret is equal to the api-key
func (m *manager) Authenticate(ctx context.Context, username, secret string) (*user.User, map[string]*authpb.Scope, error) {
if m.APIKey != secret {
return nil, nil, errtypes.InvalidCredentials("")
}

gtw, err := pool.GetGatewayServiceClient(m.GatewayAddr)
if err != nil {
return nil, nil, err
}

userResponse, err := gtw.GetUserByClaim(ctx, &user.GetUserByClaimRequest{
Claim: "username",
Value: username,
})

switch {
case err != nil:
return nil, nil, err
case userResponse.Status.Code == rpc.Code_CODE_NOT_FOUND:
return nil, nil, errtypes.NotFound(userResponse.Status.Message)
case userResponse.Status.Code != rpc.Code_CODE_OK:
return nil, nil, errtypes.InternalError(userResponse.Status.Message)
}

scope, err := scope.AddOwnerScope(nil)
if err != nil {
return nil, nil, err
}

return userResponse.GetUser(), scope, nil

}

0 comments on commit eec4c6c

Please sign in to comment.