github.com/cs3org/reva/v2@v2.27.7/pkg/cbox/user/rest/cache.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 rest
    20  
    21  import (
    22  	"encoding/json"
    23  	"errors"
    24  	"fmt"
    25  	"strconv"
    26  	"strings"
    27  	"time"
    28  
    29  	userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
    30  	"github.com/gomodule/redigo/redis"
    31  )
    32  
    33  const (
    34  	userPrefix       = "user:"
    35  	usernamePrefix   = "username:"
    36  	userIDPrefix     = "userid:"
    37  	namePrefix       = "name:"
    38  	mailPrefix       = "mail:"
    39  	uidPrefix        = "uid:"
    40  	userGroupsPrefix = "groups:"
    41  )
    42  
    43  func initRedisPool(address, username, password string) *redis.Pool {
    44  	return &redis.Pool{
    45  
    46  		MaxIdle:     50,
    47  		MaxActive:   1000,
    48  		IdleTimeout: 240 * time.Second,
    49  
    50  		Dial: func() (redis.Conn, error) {
    51  			var opts []redis.DialOption
    52  			if username != "" {
    53  				opts = append(opts, redis.DialUsername(username))
    54  			}
    55  			if password != "" {
    56  				opts = append(opts, redis.DialPassword(password))
    57  			}
    58  
    59  			c, err := redis.Dial("tcp", address, opts...)
    60  			if err != nil {
    61  				return nil, err
    62  			}
    63  			return c, err
    64  		},
    65  
    66  		TestOnBorrow: func(c redis.Conn, t time.Time) error {
    67  			_, err := c.Do("PING")
    68  			return err
    69  		},
    70  	}
    71  }
    72  
    73  func (m *manager) setVal(key, val string, expiration int) error {
    74  	conn := m.redisPool.Get()
    75  	defer conn.Close()
    76  	if conn != nil {
    77  		args := []interface{}{key, val}
    78  		if expiration != -1 {
    79  			args = append(args, "EX", expiration)
    80  		}
    81  		if _, err := conn.Do("SET", args...); err != nil {
    82  			return err
    83  		}
    84  		return nil
    85  	}
    86  	return errors.New("rest: unable to get connection from redis pool")
    87  }
    88  
    89  func (m *manager) getVal(key string) (string, error) {
    90  	conn := m.redisPool.Get()
    91  	defer conn.Close()
    92  	if conn != nil {
    93  		val, err := redis.String(conn.Do("GET", key))
    94  		if err != nil {
    95  			return "", err
    96  		}
    97  		return val, nil
    98  	}
    99  	return "", errors.New("rest: unable to get connection from redis pool")
   100  }
   101  
   102  func (m *manager) findCachedUsers(query string) ([]*userpb.User, error) {
   103  	conn := m.redisPool.Get()
   104  	defer conn.Close()
   105  	if conn != nil {
   106  		query = fmt.Sprintf("%s*%s*", userPrefix, strings.ReplaceAll(strings.ToLower(query), " ", "_"))
   107  		keys, err := redis.Strings(conn.Do("KEYS", query))
   108  		if err != nil {
   109  			return nil, err
   110  		}
   111  		var args []interface{}
   112  		for _, k := range keys {
   113  			args = append(args, k)
   114  		}
   115  
   116  		// Fetch the users for all these keys
   117  		userStrings, err := redis.Strings(conn.Do("MGET", args...))
   118  		if err != nil {
   119  			return nil, err
   120  		}
   121  		userMap := make(map[string]*userpb.User)
   122  		for _, user := range userStrings {
   123  			u := userpb.User{}
   124  			if err = json.Unmarshal([]byte(user), &u); err == nil {
   125  				userMap[u.Id.OpaqueId] = &u
   126  			}
   127  		}
   128  
   129  		var users []*userpb.User
   130  		for _, u := range userMap {
   131  			users = append(users, u)
   132  		}
   133  
   134  		return users, nil
   135  	}
   136  
   137  	return nil, errors.New("rest: unable to get connection from redis pool")
   138  }
   139  
   140  func (m *manager) fetchCachedUserDetails(uid *userpb.UserId) (*userpb.User, error) {
   141  	user, err := m.getVal(userPrefix + usernamePrefix + strings.ToLower(uid.OpaqueId))
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	u := userpb.User{}
   147  	if err = json.Unmarshal([]byte(user), &u); err != nil {
   148  		return nil, err
   149  	}
   150  	return &u, nil
   151  }
   152  
   153  func (m *manager) cacheUserDetails(u *userpb.User) error {
   154  	encodedUser, err := json.Marshal(&u)
   155  	if err != nil {
   156  		return err
   157  	}
   158  	if err = m.setVal(userPrefix+usernamePrefix+strings.ToLower(u.Id.OpaqueId), string(encodedUser), -1); err != nil {
   159  		return err
   160  	}
   161  	if err = m.setVal(userPrefix+userIDPrefix+strings.ToLower(u.Id.OpaqueId), string(encodedUser), -1); err != nil {
   162  		return err
   163  	}
   164  
   165  	if u.Mail != "" {
   166  		if err = m.setVal(userPrefix+mailPrefix+strings.ToLower(u.Mail), string(encodedUser), -1); err != nil {
   167  			return err
   168  		}
   169  	}
   170  	if u.DisplayName != "" {
   171  		if err = m.setVal(userPrefix+namePrefix+u.Id.OpaqueId+"_"+strings.ReplaceAll(strings.ToLower(u.DisplayName), " ", "_"), string(encodedUser), -1); err != nil {
   172  			return err
   173  		}
   174  	}
   175  	if u.UidNumber != 0 {
   176  		if err = m.setVal(userPrefix+uidPrefix+strconv.FormatInt(u.UidNumber, 10), string(encodedUser), -1); err != nil {
   177  			return err
   178  		}
   179  	}
   180  	return nil
   181  }
   182  
   183  func (m *manager) fetchCachedUserByParam(field, claim string) (*userpb.User, error) {
   184  	user, err := m.getVal(userPrefix + field + ":" + strings.ToLower(claim))
   185  	if err != nil {
   186  		return nil, err
   187  	}
   188  
   189  	u := userpb.User{}
   190  	if err = json.Unmarshal([]byte(user), &u); err != nil {
   191  		return nil, err
   192  	}
   193  	return &u, nil
   194  }
   195  
   196  func (m *manager) fetchCachedUserGroups(uid *userpb.UserId) ([]string, error) {
   197  	groups, err := m.getVal(userPrefix + userGroupsPrefix + strings.ToLower(uid.OpaqueId))
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	g := []string{}
   202  	if err = json.Unmarshal([]byte(groups), &g); err != nil {
   203  		return nil, err
   204  	}
   205  	return g, nil
   206  }
   207  
   208  func (m *manager) cacheUserGroups(uid *userpb.UserId, groups []string) error {
   209  	g, err := json.Marshal(&groups)
   210  	if err != nil {
   211  		return err
   212  	}
   213  	return m.setVal(userPrefix+userGroupsPrefix+strings.ToLower(uid.OpaqueId), string(g), m.conf.UserGroupsCacheExpiration*60)
   214  }