X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/32ad89ae2fe1732b659643bda98d5ab8bc6ab744..350a728ce9757d4db39d66ef4a5fe1cb304d1156:/lib/controller/localdb/logout.go diff --git a/lib/controller/localdb/logout.go b/lib/controller/localdb/logout.go index 600527405c..e1603f1448 100644 --- a/lib/controller/localdb/logout.go +++ b/lib/controller/localdb/logout.go @@ -6,25 +6,99 @@ package localdb import ( "context" + "database/sql" + "errors" + "fmt" + "net/http" + "strings" "git.arvados.org/arvados.git/lib/ctrlctx" "git.arvados.org/arvados.git/sdk/go/arvados" + "git.arvados.org/arvados.git/sdk/go/auth" + "git.arvados.org/arvados.git/sdk/go/ctxlog" + "git.arvados.org/arvados.git/sdk/go/httpserver" ) -func (conn *Conn) ExpireAPIClientAuthorization(ctx context.Context) error { - aca, err := conn.railsProxy.APIClientAuthorizationCurrent(ctx, arvados.GetOptions{}) +func logout(ctx context.Context, cluster *arvados.Cluster, opts arvados.LogoutOptions) (arvados.LogoutResponse, error) { + err := expireAPIClientAuthorization(ctx) if err != nil { - return err + ctxlog.FromContext(ctx).Errorf("attempting to expire token on logout: %q", err) + return arvados.LogoutResponse{}, httpserver.ErrorWithStatus(errors.New("could not expire token on logout"), http.StatusInternalServerError) + } + + target := opts.ReturnTo + if target == "" { + if cluster.Services.Workbench2.ExternalURL.Host != "" { + target = cluster.Services.Workbench2.ExternalURL.String() + } else { + target = cluster.Services.Workbench1.ExternalURL.String() + } + } + return arvados.LogoutResponse{RedirectLocation: target}, nil +} + +func expireAPIClientAuthorization(ctx context.Context) error { + creds, ok := auth.FromContext(ctx) + if !ok { + // Tests could be passing empty contexts + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization: credentials not found from context") + return nil } + + if len(creds.Tokens) == 0 { + // Old client may not have provided the token to expire + return nil + } + tx, err := ctrlctx.CurrentTx(ctx) if err != nil { return err } - err = tx.QueryRowxContext(ctx, "UPDATE api_client_authorizations SET expires_at=current_timestamp WHERE uuid=$1", aca.UUID).Err() + token := creds.Tokens[0] + tokenSecret := token + var tokenUuid string + if strings.HasPrefix(token, "v2/") { + tokenParts := strings.Split(token, "/") + if len(tokenParts) >= 3 { + tokenUuid = tokenParts[1] + tokenSecret = tokenParts[2] + } + } + + var retrievedUuid string + err = tx.QueryRowContext(ctx, `SELECT uuid FROM api_client_authorizations WHERE api_token=$1 AND (expires_at IS NULL OR expires_at > current_timestamp AT TIME ZONE 'UTC') LIMIT 1`, tokenSecret).Scan(&retrievedUuid) + if err == sql.ErrNoRows { + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization(%s): not found in database", token) + return nil + } else if err != nil { + ctxlog.FromContext(ctx).WithError(err).Debugf("expireAPIClientAuthorization(%s): database error", token) + return err + } + + if tokenUuid != "" && retrievedUuid != tokenUuid { + // secret part matches, but UUID doesn't -- somewhat surprising + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization(%s): secret part found, but with different UUID: %s", tokenSecret, retrievedUuid) + return nil + } + + res, err := tx.ExecContext(ctx, "UPDATE api_client_authorizations SET expires_at=current_timestamp AT TIME ZONE 'UTC' WHERE uuid=$1", retrievedUuid) if err != nil { return err } + rows, err := res.RowsAffected() + if err != nil { + return err + } + if rows == 0 { + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization(%s): no rows were updated", tokenSecret) + return fmt.Errorf("couldn't expire provided token") + } else if rows > 1 { + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization(%s): multiple (%d) rows updated", tokenSecret, rows) + } else { + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization(%s): ok", tokenSecret) + } + return nil }