github.com/argoproj/argo-cd/v3@v3.2.1/server/session/session.go (about)

     1  package session
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  
     7  	"github.com/argoproj/argo-cd/v3/util/settings"
     8  
     9  	"github.com/google/uuid"
    10  	"google.golang.org/grpc/codes"
    11  	"google.golang.org/grpc/status"
    12  
    13  	"github.com/argoproj/argo-cd/v3/pkg/apiclient/session"
    14  	"github.com/argoproj/argo-cd/v3/server/rbacpolicy"
    15  	utilio "github.com/argoproj/argo-cd/v3/util/io"
    16  	sessionmgr "github.com/argoproj/argo-cd/v3/util/session"
    17  )
    18  
    19  // Server provides a Session service
    20  type Server struct {
    21  	mgr                *sessionmgr.SessionManager
    22  	settingsMgr        *settings.SettingsManager
    23  	authenticator      Authenticator
    24  	policyEnf          *rbacpolicy.RBACPolicyEnforcer
    25  	limitLoginAttempts func() (utilio.Closer, error)
    26  }
    27  
    28  type Authenticator interface {
    29  	Authenticate(ctx context.Context) (context.Context, error)
    30  }
    31  
    32  const (
    33  	success = "success"
    34  	failure = "failure"
    35  )
    36  
    37  // NewServer returns a new instance of the Session service
    38  func NewServer(mgr *sessionmgr.SessionManager, settingsMgr *settings.SettingsManager, authenticator Authenticator, policyEnf *rbacpolicy.RBACPolicyEnforcer, rateLimiter func() (utilio.Closer, error)) *Server {
    39  	return &Server{mgr, settingsMgr, authenticator, policyEnf, rateLimiter}
    40  }
    41  
    42  // Create generates a JWT token signed by Argo CD intended for web/CLI logins of the admin user
    43  // using username/password
    44  func (s *Server) Create(_ context.Context, q *session.SessionCreateRequest) (*session.SessionResponse, error) {
    45  	if s.limitLoginAttempts != nil {
    46  		closer, err := s.limitLoginAttempts()
    47  		if err != nil {
    48  			s.mgr.IncLoginRequestCounter(failure)
    49  			return nil, err
    50  		}
    51  		defer utilio.Close(closer)
    52  	}
    53  
    54  	if q.Token != "" {
    55  		s.mgr.IncLoginRequestCounter(failure)
    56  		return nil, status.Errorf(codes.Unauthenticated, "token-based session creation no longer supported. please upgrade argocd cli to v0.7+")
    57  	}
    58  	if q.Username == "" || q.Password == "" {
    59  		s.mgr.IncLoginRequestCounter(failure)
    60  		return nil, status.Errorf(codes.Unauthenticated, "no credentials supplied")
    61  	}
    62  	err := s.mgr.VerifyUsernamePassword(q.Username, q.Password)
    63  	if err != nil {
    64  		s.mgr.IncLoginRequestCounter(failure)
    65  		return nil, err
    66  	}
    67  	uniqueId, err := uuid.NewRandom()
    68  	if err != nil {
    69  		s.mgr.IncLoginRequestCounter(failure)
    70  		return nil, err
    71  	}
    72  	argoCDSettings, err := s.settingsMgr.GetSettings()
    73  	if err != nil {
    74  		s.mgr.IncLoginRequestCounter(failure)
    75  		return nil, err
    76  	}
    77  	jwtToken, err := s.mgr.Create(
    78  		fmt.Sprintf("%s:%s", q.Username, settings.AccountCapabilityLogin),
    79  		int64(argoCDSettings.UserSessionDuration.Seconds()),
    80  		uniqueId.String())
    81  	if err != nil {
    82  		s.mgr.IncLoginRequestCounter(failure)
    83  		return nil, err
    84  	}
    85  	s.mgr.IncLoginRequestCounter(success)
    86  	return &session.SessionResponse{Token: jwtToken}, nil
    87  }
    88  
    89  // Delete an authentication cookie from the client.  This makes sense only for the Web client.
    90  func (s *Server) Delete(_ context.Context, _ *session.SessionDeleteRequest) (*session.SessionResponse, error) {
    91  	return &session.SessionResponse{Token: ""}, nil
    92  }
    93  
    94  // AuthFuncOverride overrides the authentication function and let us not require auth to receive auth.
    95  // Without this function here, ArgoCDServer.authenticate would be invoked and credentials checked.
    96  // Since this service is generally invoked when the user has _no_ credentials, that would create a
    97  // chicken-and-egg situation if we didn't place this here to allow traffic to pass through.
    98  func (s *Server) AuthFuncOverride(ctx context.Context, _ string) (context.Context, error) {
    99  	// this authenticates the user, but ignores any error, so that we have claims populated
   100  	ctx, _ = s.authenticator.Authenticate(ctx)
   101  	return ctx, nil
   102  }
   103  
   104  func (s *Server) GetUserInfo(ctx context.Context, _ *session.GetUserInfoRequest) (*session.GetUserInfoResponse, error) {
   105  	return &session.GetUserInfoResponse{
   106  		LoggedIn: sessionmgr.LoggedIn(ctx),
   107  		Username: sessionmgr.Username(ctx),
   108  		Iss:      sessionmgr.Iss(ctx),
   109  		Groups:   sessionmgr.Groups(ctx, s.policyEnf.GetScopes()),
   110  	}, nil
   111  }