github.com/cs3org/reva/v2@v2.27.7/pkg/siteacc/html/session.go (about)

     1  // Copyright 2018-2020 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 html
    20  
    21  import (
    22  	"net/http"
    23  	"net/url"
    24  	"strings"
    25  	"time"
    26  
    27  	"github.com/cs3org/reva/v2/pkg/siteacc/data"
    28  	"github.com/google/uuid"
    29  	"github.com/pkg/errors"
    30  )
    31  
    32  // Session stores all data associated with an HTML session.
    33  type Session struct {
    34  	ID            string
    35  	MigrationID   string
    36  	RemoteAddress string
    37  	CreationTime  time.Time
    38  	Timeout       time.Duration
    39  
    40  	Data map[string]interface{}
    41  
    42  	loggedInUser *SessionUser
    43  
    44  	expirationTime time.Time
    45  	halflifeTime   time.Time
    46  
    47  	sessionCookieName string
    48  }
    49  
    50  // SessionUser holds information about the logged in user
    51  type SessionUser struct {
    52  	Account *data.Account
    53  	Site    *data.Site
    54  }
    55  
    56  func getRemoteAddress(r *http.Request) string {
    57  	// Remove the port number from the remote address
    58  	remoteAddress := ""
    59  	if address := strings.Split(r.RemoteAddr, ":"); len(address) == 2 {
    60  		remoteAddress = address[0]
    61  	}
    62  	return remoteAddress
    63  }
    64  
    65  // LoggedInUser retrieves the currently logged in user or nil if none is logged in.
    66  func (sess *Session) LoggedInUser() *SessionUser {
    67  	return sess.loggedInUser
    68  }
    69  
    70  // LoginUser logs in the provided user.
    71  func (sess *Session) LoginUser(acc *data.Account, site *data.Site) {
    72  	sess.loggedInUser = &SessionUser{
    73  		Account: acc,
    74  		Site:    site,
    75  	}
    76  }
    77  
    78  // LogoutUser logs out the currently logged in user.
    79  func (sess *Session) LogoutUser() {
    80  	sess.loggedInUser = nil
    81  }
    82  
    83  // IsUserLoggedIn tells whether a user is currently logged in.
    84  func (sess *Session) IsUserLoggedIn() bool {
    85  	return sess.loggedInUser != nil
    86  }
    87  
    88  // Save stores the session ID in a cookie using a response writer.
    89  func (sess *Session) Save(cookiePath string, w http.ResponseWriter) {
    90  	fullURL, _ := url.Parse(cookiePath)
    91  	http.SetCookie(w, &http.Cookie{
    92  		Name:     sess.sessionCookieName,
    93  		Secure:   !strings.EqualFold(fullURL.Hostname(), "localhost"),
    94  		Value:    sess.ID,
    95  		MaxAge:   int(sess.Timeout / time.Second),
    96  		Domain:   fullURL.Hostname(),
    97  		Path:     fullURL.Path,
    98  		SameSite: http.SameSiteLaxMode,
    99  	})
   100  }
   101  
   102  // VerifyRequest checks whether the provided request matches the stored session.
   103  func (sess *Session) VerifyRequest(r *http.Request, verifyRemoteAddress bool) error {
   104  	cookie, err := r.Cookie(sess.sessionCookieName)
   105  	if err != nil {
   106  		return errors.Wrap(err, "unable to retrieve client session ID")
   107  	}
   108  	if cookie.Value != sess.ID {
   109  		return errors.Errorf("the session ID doesn't match")
   110  	}
   111  
   112  	if verifyRemoteAddress && sess.RemoteAddress != "" {
   113  		if !strings.EqualFold(getRemoteAddress(r), sess.RemoteAddress) {
   114  			return errors.Errorf("remote address has changed (%v != %v)", r.RemoteAddr, sess.RemoteAddress)
   115  		}
   116  	}
   117  
   118  	return nil
   119  }
   120  
   121  // HalftimePassed checks whether the session has passed the first half of its lifetime.
   122  func (sess *Session) HalftimePassed() bool {
   123  	return time.Now().After(sess.halflifeTime)
   124  }
   125  
   126  // HasExpired checks whether the session has reached is timeout.
   127  func (sess *Session) HasExpired() bool {
   128  	return time.Now().After(sess.expirationTime)
   129  }
   130  
   131  // NewSession creates a new session, giving it a random ID.
   132  func NewSession(name string, timeout time.Duration, r *http.Request) *Session {
   133  	session := &Session{
   134  		ID:                uuid.NewString(),
   135  		MigrationID:       "",
   136  		RemoteAddress:     getRemoteAddress(r),
   137  		CreationTime:      time.Now(),
   138  		Timeout:           timeout,
   139  		Data:              make(map[string]interface{}, 10),
   140  		loggedInUser:      nil,
   141  		expirationTime:    time.Now().Add(timeout),
   142  		halflifeTime:      time.Now().Add(timeout / 2),
   143  		sessionCookieName: name,
   144  	}
   145  	return session
   146  }