github.com/cs3org/reva/v2@v2.27.7/examples/plugin/json/json.go (about)

     1  // Copyright 2018-2021 CERN
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // In applying this license, CERN does not waive the privileges and immunities
    16  // granted to it by virtue of its status as an Intergovernmental Organization
    17  // or submit itself to any jurisdiction.
    18  
    19  package main
    20  
    21  import (
    22  	"context"
    23  	"encoding/json"
    24  	"errors"
    25  	"os"
    26  	"strings"
    27  
    28  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    29  	"github.com/cs3org/reva/v2/pkg/errtypes"
    30  	"github.com/cs3org/reva/v2/pkg/user"
    31  	"github.com/hashicorp/go-plugin"
    32  	"github.com/mitchellh/mapstructure"
    33  )
    34  
    35  // Manager is a real implementation of Manager interface.
    36  type Manager struct {
    37  	users []*userpb.User
    38  }
    39  
    40  type config struct {
    41  	Users string `mapstructure:"users"`
    42  }
    43  
    44  func (c *config) init() {
    45  	if c.Users == "" {
    46  		c.Users = "/etc/revad/users.json"
    47  	}
    48  }
    49  
    50  func parseConfig(m map[string]interface{}) (*config, error) {
    51  	c := &config{}
    52  	if err := mapstructure.Decode(m, c); err != nil {
    53  		return nil, err
    54  	}
    55  	c.init()
    56  	return c, nil
    57  }
    58  
    59  // Configure initializes the manager struct based on the configurations.
    60  func (m *Manager) Configure(ml map[string]interface{}) error {
    61  	c, err := parseConfig(ml)
    62  	if err != nil {
    63  		return err
    64  	}
    65  
    66  	f, err := os.ReadFile(c.Users)
    67  	if err != nil {
    68  		return err
    69  	}
    70  
    71  	users := []*userpb.User{}
    72  
    73  	err = json.Unmarshal(f, &users)
    74  	if err != nil {
    75  		return err
    76  	}
    77  
    78  	m.users = users
    79  
    80  	return nil
    81  }
    82  
    83  // GetUser returns the user based on the uid.
    84  func (m *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
    85  	for _, u := range m.users {
    86  		if (u.Id.GetOpaqueId() == uid.OpaqueId || u.Username == uid.OpaqueId) && (uid.Idp == "" || uid.Idp == u.Id.GetIdp()) {
    87  			user := *u
    88  			if skipFetchingGroups {
    89  				user.Groups = nil
    90  			}
    91  			return &user, nil
    92  		}
    93  	}
    94  	return nil, nil
    95  }
    96  
    97  // GetUserByClaim returns user based on the claim
    98  func (m *Manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) {
    99  	for _, u := range m.users {
   100  		if userClaim, err := extractClaim(u, claim); err == nil && value == userClaim {
   101  			user := *u
   102  			if skipFetchingGroups {
   103  				user.Groups = nil
   104  			}
   105  			return &user, nil
   106  		}
   107  	}
   108  	return nil, errtypes.NotFound(value)
   109  }
   110  
   111  func extractClaim(u *userpb.User, claim string) (string, error) {
   112  	switch claim {
   113  	case "mail":
   114  		return u.Mail, nil
   115  	case "username":
   116  		return u.Username, nil
   117  	case "uid":
   118  		if u.Opaque != nil && u.Opaque.Map != nil {
   119  			if uidObj, ok := u.Opaque.Map["uid"]; ok {
   120  				if uidObj.Decoder == "plain" {
   121  					return string(uidObj.Value), nil
   122  				}
   123  			}
   124  		}
   125  	}
   126  	return "", errors.New("json: invalid field")
   127  }
   128  
   129  // TODO(jfd) search Opaque? compare sub?
   130  func userContains(u *userpb.User, query string) bool {
   131  	query = strings.ToLower(query)
   132  	return strings.Contains(strings.ToLower(u.Username), query) || strings.Contains(strings.ToLower(u.DisplayName), query) ||
   133  		strings.Contains(strings.ToLower(u.Mail), query) || strings.Contains(strings.ToLower(u.Id.OpaqueId), query)
   134  }
   135  
   136  // FindUsers returns the user based on the query
   137  func (m *Manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) {
   138  	users := []*userpb.User{}
   139  	for _, u := range m.users {
   140  		if userContains(u, query) {
   141  			user := *u
   142  			if skipFetchingGroups {
   143  				user.Groups = nil
   144  			}
   145  			users = append(users, &user)
   146  		}
   147  	}
   148  	return users, nil
   149  }
   150  
   151  // GetUserGroups returns the user groups
   152  func (m *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
   153  	user, err := m.GetUser(ctx, uid, false)
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  	return user.Groups, nil
   158  }
   159  
   160  // Handshake hashicorp go-plugin handshake
   161  var Handshake = plugin.HandshakeConfig{
   162  	ProtocolVersion:  1,
   163  	MagicCookieKey:   "BASIC_PLUGIN",
   164  	MagicCookieValue: "hello",
   165  }
   166  
   167  func main() {
   168  	plugin.Serve(&plugin.ServeConfig{
   169  		HandshakeConfig: Handshake,
   170  		Plugins: map[string]plugin.Plugin{
   171  			"userprovider": &user.ProviderPlugin{Impl: &Manager{}},
   172  		},
   173  	})
   174  }