chore: Expose ParsePersonalAccessToken publicly for src-cli (#62587)

This commit is contained in:
Varun Gandhi 2024-05-10 19:32:14 +08:00 committed by GitHub
parent e37a78bbd3
commit 3a8666b99a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 146 additions and 109 deletions

View File

@ -11,8 +11,7 @@ go_library(
visibility = ["//:__subpackages__"],
deps = [
"//internal/hashutil",
"//internal/lazyregexp",
"//lib/errors",
"//lib/accesstoken",
],
)

View File

@ -7,8 +7,7 @@ import (
"encoding/hex"
"fmt"
"github.com/sourcegraph/sourcegraph/internal/lazyregexp"
"github.com/sourcegraph/sourcegraph/lib/errors"
libaccesstoken "github.com/sourcegraph/sourcegraph/lib/accesstoken"
)
// PersonalAccessTokenPrefix is the token prefix for Sourcegraph personal access tokens. Its purpose
@ -19,22 +18,7 @@ const LocalInstanceIdentifier = "local"
const InstanceIdentifierLength = 16
const InstanceIdentifierHmacKey = "instance_identifier_hmac_key" // Public as we are using HMAC for key derivation, not for authentication
var personalAccessTokenRegex = lazyregexp.New("^(?:sgp_|sgph_)?(?:[a-fA-F0-9]{16}_|local_)?([a-fA-F0-9]{40})$")
// ParsePersonalAccessToken parses a personal access token to remove prefixes and extract the <token> that is stored in the database
// Personal access tokens can take several forms:
// - <token>
// - sgp_<token>
// - sgp_<instance-identifier>_<token>
func ParsePersonalAccessToken(token string) (string, error) {
tokenMatches := personalAccessTokenRegex.FindStringSubmatch(token)
if len(tokenMatches) <= 1 {
return "", errors.New("invalid token format")
}
tokenValue := tokenMatches[1]
return tokenValue, nil
}
var ParsePersonalAccessToken = libaccesstoken.ParsePersonalAccessToken
// GeneratePersonalAccessToken generates a new personal access token.
// It returns the full token string, and the byte representation of the access token.

View File

@ -6,93 +6,6 @@ import (
"testing"
)
func TestParsePersonalAccessToken(t *testing.T) {
type args struct {
token string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
// Valid test cases
{
name: "no prefix",
args: args{token: "abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgp_ prefix",
args: args{token: "sgp_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix",
args: args{token: "sgph_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix and instance-identifier",
args: args{token: "sgph_0123456789abcdef_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix and local instance-identifier",
args: args{token: "sgph_local_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
// Error cases
{
name: "no prefix, invalid length",
args: args{token: "abc123"},
want: "",
wantErr: true,
},
{
name: "invalid prefix, invalid length",
args: args{token: "sgptest_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
{
name: "prefix, invalid length",
args: args{token: "sgp_abcdef1234abcdef1234abcdef1234abcdef12345"},
want: "",
wantErr: true,
},
{
name: "too-short instance identifer",
args: args{token: "sgph_01234_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
{
name: "too-long instance identifer",
args: args{token: "sgph_0123456789abcdef0_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParsePersonalAccessToken(tt.args.token)
if (err != nil) != tt.wantErr {
t.Errorf("ParsePersonalAccessToken() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ParsePersonalAccessToken() = %v, want %v", got, tt.want)
}
})
}
}
func TestGeneratePersonalAccessToken(t *testing.T) {
type args struct {
licenseKey string

View File

@ -0,0 +1,19 @@
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//dev:go_defs.bzl", "go_test")
go_library(
name = "accesstoken",
srcs = ["personal_access_token.go"],
importpath = "github.com/sourcegraph/sourcegraph/lib/accesstoken",
visibility = ["//visibility:public"],
deps = [
"//lib/errors",
"@com_github_grafana_regexp//:regexp",
],
)
go_test(
name = "accesstoken_test",
srcs = ["personal_access_token_test.go"],
embed = [":accesstoken"],
)

View File

@ -0,0 +1,29 @@
// Package accesstoken is exposed in lib/ for usage in src-cli
package accesstoken
import (
"sync"
"github.com/grafana/regexp" // avoid pulling in internal lazyregexp package
"github.com/sourcegraph/sourcegraph/lib/errors"
)
var makePersonalAccessTokenRegex = sync.OnceValue[*regexp.Regexp](func() *regexp.Regexp {
return regexp.MustCompile("^(?:sgp_|sgph_)?(?:[a-fA-F0-9]{16}_|local_)?([a-fA-F0-9]{40})$")
})
// ParsePersonalAccessToken parses a personal access token to remove prefixes and extract the <token> that is stored in the database
// Personal access tokens can take several forms:
// - <token>
// - sgp_<token>
// - sgp_<instance-identifier>_<token>
func ParsePersonalAccessToken(token string) (string, error) {
tokenMatches := makePersonalAccessTokenRegex().FindStringSubmatch(token)
if len(tokenMatches) <= 1 {
return "", errors.New("invalid token format")
}
tokenValue := tokenMatches[1]
return tokenValue, nil
}

View File

@ -0,0 +1,92 @@
package accesstoken
import (
"testing"
)
func TestParsePersonalAccessToken(t *testing.T) {
type args struct {
token string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
// Valid test cases
{
name: "no prefix",
args: args{token: "abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgp_ prefix",
args: args{token: "sgp_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix",
args: args{token: "sgph_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix and instance-identifier",
args: args{token: "sgph_0123456789abcdef_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
{
name: "sgph_ prefix and local instance-identifier",
args: args{token: "sgph_local_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "abcdef1234abcdef1234abcdef1234abcdef1234",
wantErr: false,
},
// Error cases
{
name: "no prefix, invalid length",
args: args{token: "abc123"},
want: "",
wantErr: true,
},
{
name: "invalid prefix, invalid length",
args: args{token: "sgptest_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
{
name: "prefix, invalid length",
args: args{token: "sgp_abcdef1234abcdef1234abcdef1234abcdef12345"},
want: "",
wantErr: true,
},
{
name: "too-short instance identifer",
args: args{token: "sgph_01234_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
{
name: "too-long instance identifer",
args: args{token: "sgph_0123456789abcdef0_abcdef1234abcdef1234abcdef1234abcdef1234"},
want: "",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParsePersonalAccessToken(tt.args.token)
if (err != nil) != tt.wantErr {
t.Errorf("ParsePersonalAccessToken() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("ParsePersonalAccessToken() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -71,6 +71,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/hexops/gotextdiff v1.0.3 // indirect
github.com/hexops/valast v1.4.3 // indirect

View File

@ -125,8 +125,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g=
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=