}
func (ctrl *pamLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
+ return arvados.LoginResponse{}, errors.New("interactive login is not available")
+}
+
+func (ctrl *pamLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
errorMessage := ""
+ sentPassword := false
tx, err := pam.StartFunc(ctrl.Cluster.Login.PAMService, opts.Username, func(style pam.Style, message string) (string, error) {
ctxlog.FromContext(ctx).Debugf("pam conversation: style=%v message=%q", style, message)
switch style {
ctxlog.FromContext(ctx).WithField("Message", message).Info("pam.TextInfo")
return "", nil
case pam.PromptEchoOn, pam.PromptEchoOff:
+ sentPassword = true
return opts.Password, nil
default:
return "", fmt.Errorf("unrecognized message style %d", style)
}
})
if err != nil {
- return arvados.LoginResponse{}, err
+ return arvados.APIClientAuthorization{}, err
}
err = tx.Authenticate(pam.DisallowNullAuthtok)
if err != nil {
- return arvados.LoginResponse{}, httpserver.ErrorWithStatus(err, http.StatusUnauthorized)
+ err = fmt.Errorf("PAM: %s", err)
+ if errorMessage != "" {
+ // Perhaps the error message in the
+ // conversation is helpful.
+ err = fmt.Errorf("%s; %q", err, errorMessage)
+ }
+ if sentPassword {
+ err = fmt.Errorf("%s (with username %q and password)", err, opts.Username)
+ } else {
+ // This might hint that the username was
+ // invalid.
+ err = fmt.Errorf("%s (with username %q; password was never requested by PAM service)", err, opts.Username)
+ }
+ return arvados.APIClientAuthorization{}, httpserver.ErrorWithStatus(err, http.StatusUnauthorized)
}
if errorMessage != "" {
- return arvados.LoginResponse{}, httpserver.ErrorWithStatus(errors.New(errorMessage), http.StatusUnauthorized)
+ return arvados.APIClientAuthorization{}, httpserver.ErrorWithStatus(errors.New(errorMessage), http.StatusUnauthorized)
}
user, err := tx.GetItem(pam.User)
if err != nil {
- return arvados.LoginResponse{}, err
+ return arvados.APIClientAuthorization{}, err
}
email := user
if domain := ctrl.Cluster.Login.PAMDefaultEmailDomain; domain != "" && !strings.Contains(email, "@") {
// Send a fake ReturnTo value instead of the caller's
// opts.ReturnTo. We won't follow the resulting
// redirect target anyway.
- ReturnTo: opts.Remote + ",https://none.invalid",
+ ReturnTo: ",https://none.invalid",
AuthInfo: rpc.UserSessionAuthInfo{
Username: user,
Email: email,
},
})
if err != nil {
- return arvados.LoginResponse{}, err
+ return arvados.APIClientAuthorization{}, err
}
target, err := url.Parse(resp.RedirectLocation)
if err != nil {
- return arvados.LoginResponse{}, err
+ return arvados.APIClientAuthorization{}, err
}
- resp.Token = target.Query().Get("api_token")
- resp.RedirectLocation = ""
- return resp, err
+ token := target.Query().Get("api_token")
+ return ctrl.RailsProxy.APIClientAuthorizationCurrent(auth.NewContext(ctx, auth.NewCredentials(token)), arvados.GetOptions{})
}