X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/94b3b18d028468440d793c2f78da61acc0ba4270..ffae77ee85f7a6dd4a095298aaa9dba145c98bdd:/lib/controller/localdb/logout.go diff --git a/lib/controller/localdb/logout.go b/lib/controller/localdb/logout.go index 417fdc0667..e1603f1448 100644 --- a/lib/controller/localdb/logout.go +++ b/lib/controller/localdb/logout.go @@ -6,40 +6,83 @@ 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 { - creds, ok := auth.FromContext(ctx) +func logout(ctx context.Context, cluster *arvados.Cluster, opts arvados.LogoutOptions) (arvados.LogoutResponse, error) { + err := expireAPIClientAuthorization(ctx) + if err != nil { + 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 { - return errors.New("credentials not found from context") + // Tests could be passing empty contexts + ctxlog.FromContext(ctx).Debugf("expireAPIClientAuthorization: credentials not found from context") + return nil } - if len(creds.Tokens) < 1 { - return errors.New("no tokens found to expire") + 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 } token := creds.Tokens[0] - tokensecret := token - if strings.Contains(token, "/") { - tokenparts := strings.Split(token, "/") - if len(tokenparts) >= 3 { - tokensecret = tokenparts[2] + tokenSecret := token + var tokenUuid string + if strings.HasPrefix(token, "v2/") { + tokenParts := strings.Split(token, "/") + if len(tokenParts) >= 3 { + tokenUuid = tokenParts[1] + tokenSecret = tokenParts[2] } } - tx, err := ctrlctx.CurrentTx(ctx) - if err != nil { + 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 } - res, err := tx.ExecContext(ctx, "UPDATE api_client_authorizations SET expires_at=current_timestamp WHERE api_token=$1", tokensecret) + 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 } @@ -48,8 +91,13 @@ func (conn *Conn) ExpireAPIClientAuthorization(ctx context.Context) error { if err != nil { return err } - if rows != 1 { - return fmt.Errorf("token expiration affected rows: %d - token: %s", rows, token) + 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