2FA: Rename "Auth Secret" to "App Password" for more clarity #782 #808

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2024-01-19 18:10:01 +01:00
parent d3a67a6694
commit a4e2bb33b9
21 changed files with 171 additions and 162 deletions

View file

@ -39,6 +39,8 @@ export class Session extends RestModel {
UserUID: "",
UserName: "",
UserAgent: "",
ClientUID: "",
ClientName: "",
AuthProvider: "",
AuthMethod: "",
AuthDomain: "",

View file

@ -22,6 +22,7 @@ export const Providers = () => {
local: $gettext("Local"),
client: $gettext("Client"),
client_credentials: $gettext("Client Credentials"),
application: $gettext("Application"),
access_token: $gettext("Access Token"),
password: $gettext("Local"),
ldap: $gettext("LDAP/AD"),
@ -36,11 +37,12 @@ export const Methods = () => {
return {
"": $gettext("Default"),
default: $gettext("Default"),
personal: $gettext("Personal"),
access_token: $gettext("Access Token"),
session: $gettext("Session"),
totp: "TOTP/2FA",
personal: $gettext("Personal"),
client: $gettext("Client"),
access_token: $gettext("Access Token"),
oauth2: "OAuth2",
totp: "TOTP/2FA",
oidc: "OIDC",
};
};

View file

@ -17,7 +17,7 @@ import (
var AuthAddFlags = []cli.Flag{
cli.StringFlag{
Name: "name, n",
Usage: "access `TOKEN` name to help identify the client application",
Usage: "`CLIENT` name to help identify the application",
},
cli.StringFlag{
Name: "scope, s",
@ -25,19 +25,20 @@ var AuthAddFlags = []cli.Flag{
},
cli.Int64Flag{
Name: "expires, e",
Usage: "authentication `LIFETIME` in seconds, after which the access token expires (-1 to disable the limit)",
Usage: "authentication `LIFETIME` in seconds, after which access expires (-1 to disable the limit)",
Value: entity.UnixYear,
},
}
// AuthAddCommand configures the command name, flags, and action.
var AuthAddCommand = cli.Command{
Name: "add",
Usage: "Creates a new access token for client authentication",
Description: "If you provide a username as argument, a personal access token for registered users will be created.",
ArgsUsage: "[username]",
Flags: AuthAddFlags,
Action: authAddAction,
Name: "add",
Usage: "Adds a new authentication secret for client applications",
Description: "If you specify a username as argument, an app password will be created for this user account." +
" It can be used as a password replacement to grant limited access to client applications.",
ArgsUsage: "[username]",
Flags: AuthAddFlags,
Action: authAddAction,
}
// authAddAction shows detailed session information.
@ -89,22 +90,21 @@ func authAddAction(ctx *cli.Context) error {
authScope = clean.Scope(res)
}
// Create session with client access token.
sess, err := entity.CreateClientAccessToken(clientName, ctx.Int64("expires"), authScope, user)
// Create session and show the authentication secret.
sess, err := entity.AddClientAuthentication(clientName, ctx.Int64("expires"), authScope, user)
if err != nil {
return fmt.Errorf("failed to create access token: %s", err)
return fmt.Errorf("failed to create authentication secret: %s", err)
} else {
// Show client authentication credentials.
if sess.UserUID == "" {
fmt.Printf("\nPLEASE WRITE DOWN THE FOLLOWING RANDOMLY GENERATED CLIENT ACCESS TOKEN, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:\n")
fmt.Printf("\nPLEASE WRITE DOWN THE FOLLOWING RANDOMLY GENERATED ACCESS TOKEN, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:\n")
fmt.Printf("\n%s\n", report.Credentials("Access Token", sess.AuthToken(), "Authorization Scope", sess.Scope()))
} else {
fmt.Printf("\nPLEASE WRITE DOWN THE FOLLOWING RANDOMLY GENERATED PERSONAL ACCESS TOKEN, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:\n")
fmt.Printf("\nPLEASE WRITE DOWN THE FOLLOWING RANDOMLY GENERATED APP PASSWORD, AS YOU WILL NOT BE ABLE TO SEE IT AGAIN:\n")
fmt.Printf("\n%s\n", report.Credentials("App Password", sess.AuthToken(), "Authorization Scope", sess.Scope()))
}
result := report.Credentials("Access Token", sess.AuthToken(), "Authorization Scope", sess.Scope())
fmt.Printf("\n%s\n", result)
}
return err

View file

@ -25,7 +25,7 @@ func authListAction(ctx *cli.Context) error {
return CallWithDependencies(ctx, func(conf *config.Config) error {
var rows [][]string
cols := []string{"Session ID", "Session User", "Session Client", "Authentication Method", "Scope", "Login IP", "Current IP", "Last Active", "Created At", "Expires At"}
cols := []string{"Session ID", "Session User", "Client Name", "Authentication Method", "Scope", "Login IP", "Current IP", "Last Active", "Created At", "Expires At"}
if ctx.Bool("tokens") {
cols = append(cols, "Preview Token", "Download Token")

View file

@ -87,7 +87,7 @@ func clientsAddAction(ctx *cli.Context) error {
log.Infof("successfully registered new client %s", clean.LogQuote(client.ClientName))
// Display client details.
cols := []string{"Client ID", "Client Name", "Client User", "Authentication Method", "Role", "Scope", "Enabled", "Authentication Expires", "Created At"}
cols := []string{"Client ID", "Client Name", "Authentication Method", "User", "Role", "Scope", "Enabled", "Authentication Expires", "Created At"}
rows := make([][]string, 1)
var authExpires string
@ -104,8 +104,8 @@ func clientsAddAction(ctx *cli.Context) error {
rows[0] = []string{
client.UID(),
client.Name(),
client.UserInfo(),
client.AuthInfo(),
client.UserInfo(),
client.AclRole().String(),
client.Scope(),
report.Bool(client.AuthEnabled, report.Yes, report.No),

View file

@ -23,7 +23,7 @@ var ClientsListCommand = cli.Command{
// clientsListAction lists registered client applications
func clientsListAction(ctx *cli.Context) error {
return CallWithDependencies(ctx, func(conf *config.Config) error {
cols := []string{"Client ID", "Client Name", "Client User", "Authentication Method", "Role", "Scope", "Enabled", "Authentication Expires", "Created At"}
cols := []string{"Client ID", "Client Name", "Authentication Method", "User", "Role", "Scope", "Enabled", "Authentication Expires", "Created At"}
// Fetch clients from database.
clients, err := query.Clients(ctx.Int("n"), 0, "", ctx.Args().First())
@ -58,8 +58,8 @@ func clientsListAction(ctx *cli.Context) error {
rows[i] = []string{
client.UID(),
client.Name(),
client.UserInfo(),
client.AuthInfo(),
client.UserInfo(),
client.AclRole().String(),
client.Scope(),
report.Bool(client.AuthEnabled, report.Yes, report.No),

View file

@ -62,7 +62,7 @@ func TestClientsListCommand(t *testing.T) {
// Check command output for plausibility.
// t.Logf(output)
assert.NoError(t, err)
assert.Contains(t, output, "Client ID;Client Name;Client User;Authentication Method;Role;Scope;")
assert.Contains(t, output, "Client ID;Client Name;Authentication Method;User;Role;Scope;Enabled;Authentication Expires;Created At")
assert.Contains(t, output, "Monitoring")
assert.Contains(t, output, "metrics")
assert.NotContains(t, output, "bob")

View file

@ -1,9 +1,10 @@
package commands
import (
"testing"
"github.com/photoprism/photoprism/pkg/capture"
"github.com/stretchr/testify/assert"
"testing"
)
func TestUsersModCommand(t *testing.T) {

View file

@ -1,9 +1,10 @@
package commands
import (
"testing"
"github.com/photoprism/photoprism/pkg/capture"
"github.com/stretchr/testify/assert"
"testing"
)
func TestUsersRemoveCommand(t *testing.T) {

View file

@ -1,9 +1,10 @@
package commands
import (
"testing"
"github.com/photoprism/photoprism/pkg/capture"
"github.com/stretchr/testify/assert"
"testing"
)
func TestUsersCommand(t *testing.T) {

View file

@ -1,42 +0,0 @@
package entity
import (
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/rnd"
)
// NewClientAccessToken returns a new access token session instance
// that can be used to access the API with unregistered clients.
func NewClientAccessToken(clientName string, lifetime int64, scope string, user *User) *Session {
sess := NewSession(lifetime, 0)
if clientName == "" {
clientName = rnd.Name()
}
sess.SetClientName(clientName)
sess.SetProvider(authn.ProviderAccessToken)
sess.SetScope(scope)
if user != nil {
sess.SetUser(user)
sess.SetAuthToken(rnd.AuthSecret())
sess.SetMethod(authn.MethodPersonal)
} else {
sess.SetMethod(authn.MethodDefault)
}
return sess
}
// CreateClientAccessToken initializes and creates a new access token session
// that can be used to access the API with unregistered clients.
func CreateClientAccessToken(clientName string, lifetime int64, scope string, user *User) (*Session, error) {
sess := NewClientAccessToken(clientName, lifetime, scope, user)
if err := sess.Create(); err != nil {
return nil, err
}
return sess, nil
}

View file

@ -0,0 +1,41 @@
package entity
import (
"github.com/photoprism/photoprism/pkg/authn"
"github.com/photoprism/photoprism/pkg/rnd"
)
// NewClientAuthentication returns a new session that authenticates a client application.
func NewClientAuthentication(clientName string, lifetime int64, scope string, user *User) *Session {
sess := NewSession(lifetime, 0)
if clientName == "" {
clientName = rnd.Name()
}
sess.SetClientName(clientName)
sess.SetScope(scope)
if user != nil {
sess.SetUser(user)
sess.SetAuthToken(rnd.AppPassword())
sess.SetProvider(authn.ProviderApplication)
sess.SetMethod(authn.MethodDefault)
} else {
sess.SetProvider(authn.ProviderAccessToken)
sess.SetMethod(authn.MethodOAuth2)
}
return sess
}
// AddClientAuthentication creates a new session for authenticating a client application.
func AddClientAuthentication(clientName string, lifetime int64, scope string, user *User) (*Session, error) {
sess := NewClientAuthentication(clientName, lifetime, scope, user)
if err := sess.Create(); err != nil {
return nil, err
}
return sess, nil
}

View file

@ -6,9 +6,9 @@ import (
"github.com/stretchr/testify/assert"
)
func TestNewClientAccessToken(t *testing.T) {
func TestNewClientAuthentication(t *testing.T) {
t.Run("Anonymous", func(t *testing.T) {
sess := NewClientAccessToken("Anonymous", UnixDay, "metrics", nil)
sess := NewClientAuthentication("Anonymous", UnixDay, "metrics", nil)
if sess == nil {
t.Fatal("session must not be nil")
@ -23,7 +23,7 @@ func TestNewClientAccessToken(t *testing.T) {
t.Fatal("user must not be nil")
}
sess := NewClientAccessToken("alice", UnixDay, "metrics", user)
sess := NewClientAuthentication("alice", UnixDay, "metrics", user)
if sess == nil {
t.Fatal("session must not be nil")
@ -38,7 +38,7 @@ func TestNewClientAccessToken(t *testing.T) {
t.Fatal("user must not be nil")
}
sess := NewClientAccessToken("alice", UnixDay, "", user)
sess := NewClientAuthentication("alice", UnixDay, "", user)
if sess == nil {
t.Fatal("session must not be nil")
@ -53,7 +53,7 @@ func TestNewClientAccessToken(t *testing.T) {
t.Fatal("user must not be nil")
}
sess := NewClientAccessToken("", 0, "metrics", user)
sess := NewClientAuthentication("", 0, "metrics", user)
if sess == nil {
t.Fatal("session must not be nil")
@ -63,9 +63,9 @@ func TestNewClientAccessToken(t *testing.T) {
})
}
func TestCreateClientAccessToken(t *testing.T) {
func TestAddClientAuthentication(t *testing.T) {
t.Run("Anonymous", func(t *testing.T) {
sess, err := CreateClientAccessToken("", UnixDay, "metrics", nil)
sess, err := AddClientAuthentication("", UnixDay, "metrics", nil)
assert.NoError(t, err)
@ -82,7 +82,7 @@ func TestCreateClientAccessToken(t *testing.T) {
t.Fatal("user must not be nil")
}
sess, err := CreateClientAccessToken("My Client App Token", UnixDay, "metrics", user)
sess, err := AddClientAuthentication("My Client App Token", UnixDay, "metrics", user)
assert.NoError(t, err)

View file

@ -44,10 +44,10 @@ var Auth = func(f form.Login, m *Session, c *gin.Context) (user *User, provider
func AuthSession(f form.Login, c *gin.Context) (sess *Session, user *User, err error) {
if f.Password == "" {
// Abort authentication if no token was provided.
return nil, nil, fmt.Errorf("no auth secret provided")
} else if !rnd.IsAuthSecret(f.Password, true) {
return nil, nil, fmt.Errorf("no password provided")
} else if !rnd.IsAppPassword(f.Password, true) {
// Abort authentication if token doesn't match expected format.
return nil, nil, fmt.Errorf("auth secret does not match expected format")
return nil, nil, fmt.Errorf("password does not match expected format")
}
// Get session ID for the auth token provided.
@ -58,7 +58,7 @@ func AuthSession(f form.Login, c *gin.Context) (sess *Session, user *User, err e
// Log error and return nil if no matching session was found.
if sess == nil || err != nil {
return nil, nil, fmt.Errorf("invalid auth secret")
return nil, nil, fmt.Errorf("invalid password")
}
// Update the client IP and the user agent from
@ -113,14 +113,14 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (authn.Prov
if !authUser.IsRegistered() || authUser.UserUID != user.UserUID {
message := "incorrect user"
limiter.Login.Reserve(clientIp)
event.AuditErr([]string{clientIp, "session %s", "login as %s with auth secret", message}, m.RefID, clean.LogQuote(userName))
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(userName))
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
m.Status = http.StatusUnauthorized
return authn.ProviderNone, i18n.Error(i18n.ErrInvalidCredentials)
} else if !authSess.IsClient() || !authSess.HasScope(acl.ResourceSessions.String()) {
message := "unauthorized"
limiter.Login.Reserve(clientIp)
event.AuditErr([]string{clientIp, "session %s", "login as %s with auth secret", message}, m.RefID, clean.LogQuote(userName))
event.AuditErr([]string{clientIp, "session %s", "login as %s with app password", message}, m.RefID, clean.LogQuote(userName))
event.LoginError(clientIp, "api", userName, m.UserAgent, message)
m.Status = http.StatusUnauthorized
return authn.ProviderNone, i18n.Error(i18n.ErrInvalidCredentials)
@ -129,9 +129,9 @@ func AuthLocal(user *User, f form.Login, m *Session, c *gin.Context) (authn.Prov
m.ClientName = authSess.ClientName
m.SetScope(authSess.Scope())
m.SetMethod(authn.MethodSession)
event.AuditInfo([]string{clientIp, "session %s", "login as %s with auth secret", "succeeded"}, m.RefID, clean.LogQuote(userName))
event.AuditInfo([]string{clientIp, "session %s", "login as %s with app password", "succeeded"}, m.RefID, clean.LogQuote(userName))
event.LoginInfo(clientIp, "api", userName, m.UserAgent)
return authn.ProviderClient, err
return authn.ProviderApplication, err
}
}

View file

@ -15,11 +15,11 @@ import (
)
func TestAuthSession(t *testing.T) {
t.Run("RandomAuthSecret", func(t *testing.T) {
t.Run("RandomAppPassword", func(t *testing.T) {
// Create test request form.
f := form.Login{
UserName: "alice",
Password: rnd.AuthSecret(),
Password: rnd.AppPassword(),
}
// Create test request context.

View file

@ -83,8 +83,8 @@ func WebDAVAuth(conf *config.Config) gin.HandlerFunc {
authToken := header.AuthToken(c)
// Use the value provided in the password field as auth token if no username was provided
// and the format matches auth secrets e.g. "OXiV72-wTtiL9-d04jO7-X7XP4p".
if username != "" && authToken == "" && rnd.IsAuthSecret(password, true) {
// and the format matches an app password e.g. "OXiV72-wTtiL9-d04jO7-X7XP4p".
if username != "" && authToken == "" && rnd.IsAppPassword(password, true) {
authToken = password
}

View file

@ -133,14 +133,14 @@ func TestWebDAVAuth(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, c.Writer.Status())
assert.Equal(t, BasicAuthRealm, c.Writer.Header().Get("WWW-Authenticate"))
})
t.Run("InvalidAuthSecret", func(t *testing.T) {
t.Run("InvalidAppPassword", func(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = &http.Request{
Header: make(http.Header),
}
header.SetAuthorization(c.Request, rnd.AuthSecret())
header.SetAuthorization(c.Request, rnd.AppPassword())
webdavAuthCache.Flush()
webdavHandler(c)
@ -228,18 +228,18 @@ func TestWebDAVAuthSession(t *testing.T) {
assert.Equal(t, http.StatusOK, c.Writer.Status())
assert.Equal(t, "", c.Writer.Header().Get("WWW-Authenticate"))
})
t.Run("InvalidAuthSecret", func(t *testing.T) {
t.Run("InvalidAppPassword", func(t *testing.T) {
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = &http.Request{
Header: make(http.Header),
}
authToken := rnd.AuthSecret()
authId := rnd.SessionID(authToken)
appPassword := rnd.AppPassword()
authId := rnd.SessionID(appPassword)
// Get session with invalid auth secret.
sess, user, sid, cached := WebDAVAuthSession(c, authToken)
// Get session with invalid app password.
sess, user, sid, cached := WebDAVAuthSession(c, appPassword)
// Check result.
assert.Nil(t, sess)

View file

@ -16,6 +16,7 @@ const (
ProviderDefault ProviderType = "default"
ProviderClient ProviderType = "client"
ProviderClientCredentials ProviderType = "client_credentials"
ProviderApplication ProviderType = "application"
ProviderAccessToken ProviderType = "access_token"
ProviderLocal ProviderType = "local"
ProviderLDAP ProviderType = "ldap"
@ -38,6 +39,7 @@ var LocalProviders = list.List{
var ClientProviders = list.List{
string(ProviderClient),
string(ProviderClientCredentials),
string(ProviderApplication),
string(ProviderAccessToken),
}

View file

@ -78,7 +78,7 @@ func TestAuthToken(t *testing.T) {
bearerToken := BearerToken(c)
assert.Equal(t, "", bearerToken)
})
t.Run("AuthSecret", func(t *testing.T) {
t.Run("AppPassword", func(t *testing.T) {
gin.SetMode(gin.TestMode)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
@ -86,8 +86,8 @@ func TestAuthToken(t *testing.T) {
Header: make(http.Header),
}
// Set X-Auth-Token header to a random value generated by rnd.AuthSecret().
expected := rnd.AuthSecret()
// Set X-Auth-Token header to a random value generated by rnd.AppPassword().
expected := rnd.AppPassword()
c.Request.Header.Add(XAuthToken, expected)
// Check header for expected token.

View file

@ -9,13 +9,13 @@ import (
)
const (
SessionIdLength = 64
AuthTokenLength = 48
AuthSecretLength = 27
AuthSecretSeparator = '-'
SessionIdLength = 64
AuthTokenLength = 48
AppPasswordLength = 27
AppPasswordSeparator = '-'
)
// AuthToken returns a random hexadecimal character string that can be used for authentication purposes.
// AuthToken generates a random hexadecimal character token for authenticating client applications.
//
// Examples: 9fa8e562564dac91b96881040e98f6719212a1a364e0bb25
func AuthToken() string {
@ -37,18 +37,19 @@ func IsAuthToken(s string) bool {
return false
}
// AuthSecret returns a random, human-friendly string that can be used instead of a regular auth token.
// It is separated by 3 dashes for better readability and has a total length of 27 characters.
// AppPassword generates a random, human-friendly authentication token that can also be used as
// password replacement for client applications. It is separated by 3 dashes for better readability
// and has a total length of 27 characters.
//
// Example: OXiV72-wTtiL9-d04jO7-X7XP4p
func AuthSecret() string {
func AppPassword() string {
m := big.NewInt(int64(len(CharsetBase62)))
b := make([]byte, 0, AuthSecretLength)
b := make([]byte, 0, AppPasswordLength)
for i := 0; i < AuthSecretLength; i++ {
for i := 0; i < AppPasswordLength; i++ {
if (i+1)%7 == 0 {
b = append(b, AuthSecretSeparator)
} else if i == AuthSecretLength-1 {
b = append(b, AppPasswordSeparator)
} else if i == AppPasswordLength-1 {
b = append(b, CharsetBase62[crc32.ChecksumIEEE(b)%62])
return string(b)
} else if r, err := rand.Int(rand.Reader, m); err == nil {
@ -59,17 +60,17 @@ func AuthSecret() string {
return string(b)
}
// IsAuthSecret checks if the string might be a valid auth secret.
func IsAuthSecret(s string, verifyChecksum bool) bool {
// IsAppPassword checks if the string might be a valid app password.
func IsAppPassword(s string, verifyChecksum bool) bool {
// Verify token length.
if len(s) != AuthSecretLength {
if len(s) != AppPasswordLength {
return false
}
// Check characters.
sep := 0
for _, r := range s {
if r == AuthSecretSeparator {
if r == AppPasswordSeparator {
sep++
} else if (r < '0' || r > '9') && (r < 'A' || r > 'Z') && (r < 'a' || r > 'z') {
return false
@ -77,25 +78,25 @@ func IsAuthSecret(s string, verifyChecksum bool) bool {
}
// Check number of separators.
if sep != AuthSecretLength/7 {
if sep != AppPasswordLength/7 {
return false
} else if !verifyChecksum {
return true
}
// Verify token checksum.
return s[AuthSecretLength-1] == CharsetBase62[crc32.ChecksumIEEE([]byte(s[:AuthSecretLength-1]))%62]
return s[AppPasswordLength-1] == CharsetBase62[crc32.ChecksumIEEE([]byte(s[:AppPasswordLength-1]))%62]
}
// IsAuthAny checks if the string might be a valid auth token or secret.
// IsAuthAny checks if the string might be a valid auth token or app password.
func IsAuthAny(s string) bool {
// Check if string might be a regular auth token.
if IsAuthToken(s) {
return true
}
// Check if string might be a human-friendly auth secret.
if IsAuthSecret(s, false) {
// Check if string might be a human-friendly app password.
if IsAppPassword(s, false) {
return true
}

View file

@ -43,72 +43,72 @@ func BenchmarkIsAuthToken(b *testing.B) {
}
}
func TestAuthSecret(t *testing.T) {
func TestAppPassword(t *testing.T) {
for n := 0; n < 10; n++ {
s := AuthSecret()
t.Logf("AuthSecret %d: %s", n, s)
assert.Equal(t, AuthSecretLength, len(s))
s := AppPassword()
t.Logf("AppPassword %d: %s", n, s)
assert.Equal(t, AppPasswordLength, len(s))
}
}
func BenchmarkAuthSecret(b *testing.B) {
func BenchmarkAppPassword(b *testing.B) {
for n := 0; n < b.N; n++ {
AuthSecret()
AppPassword()
}
}
func TestIsAuthSecret(t *testing.T) {
func TestIsAppPasswordt(t *testing.T) {
t.Run("VerifyChecksum", func(t *testing.T) {
assert.True(t, IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", true))
assert.True(t, IsAuthSecret("9q2JHc-P0LzNE-xzvY9j-vMoefj", true))
assert.False(t, IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3Xha", true))
assert.False(t, IsAuthSecret("9q2JHc-P0LzNE-xzvY9j-vMoef2", true))
assert.True(t, IsAuthSecret(AuthSecret(), true))
assert.True(t, IsAuthSecret(AuthSecret(), true))
assert.False(t, IsAuthSecret(AuthToken(), true))
assert.False(t, IsAuthSecret(AuthToken(), true))
assert.False(t, IsAuthSecret(SessionID(AuthToken()), true))
assert.False(t, IsAuthSecret(SessionID(AuthToken()), true))
assert.False(t, IsAuthSecret("55785BAC-9H4B-4747-B090-EE123FFEE437", true))
assert.False(t, IsAuthSecret("4B1FEF2D1CF4A5BE38B263E0637EDEAD", true))
assert.False(t, IsAuthSecret("69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac2", true))
assert.False(t, IsAuthSecret("", true))
assert.True(t, IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", true))
assert.True(t, IsAppPassword("9q2JHc-P0LzNE-xzvY9j-vMoefj", true))
assert.False(t, IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3Xha", true))
assert.False(t, IsAppPassword("9q2JHc-P0LzNE-xzvY9j-vMoef2", true))
assert.True(t, IsAppPassword(AppPassword(), true))
assert.True(t, IsAppPassword(AppPassword(), true))
assert.False(t, IsAppPassword(AuthToken(), true))
assert.False(t, IsAppPassword(AuthToken(), true))
assert.False(t, IsAppPassword(SessionID(AuthToken()), true))
assert.False(t, IsAppPassword(SessionID(AuthToken()), true))
assert.False(t, IsAppPassword("55785BAC-9H4B-4747-B090-EE123FFEE437", true))
assert.False(t, IsAppPassword("4B1FEF2D1CF4A5BE38B263E0637EDEAD", true))
assert.False(t, IsAppPassword("69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac2", true))
assert.False(t, IsAppPassword("", true))
})
t.Run("IgnoreChecksum", func(t *testing.T) {
assert.True(t, IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", false))
assert.True(t, IsAuthSecret("9q2JHc-P0LzNE-xzvY9j-vMoefj", false))
assert.True(t, IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3Xha", false))
assert.True(t, IsAuthSecret("9q2JHc-P0LzNE-xzvY9j-vMoef2", false))
assert.True(t, IsAuthSecret(AuthSecret(), false))
assert.True(t, IsAuthSecret(AuthSecret(), false))
assert.False(t, IsAuthSecret(AuthToken(), false))
assert.False(t, IsAuthSecret(AuthToken(), false))
assert.False(t, IsAuthSecret(SessionID(AuthToken()), false))
assert.False(t, IsAuthSecret(SessionID(AuthToken()), false))
assert.False(t, IsAuthSecret("55785BAC-9H4B-4747-B090-EE123FFEE437", false))
assert.False(t, IsAuthSecret("4B1FEF2D1CF4A5BE38B263E0637EDEAD", false))
assert.False(t, IsAuthSecret("69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac2", false))
assert.False(t, IsAuthSecret("", false))
assert.True(t, IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", false))
assert.True(t, IsAppPassword("9q2JHc-P0LzNE-xzvY9j-vMoefj", false))
assert.True(t, IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3Xha", false))
assert.True(t, IsAppPassword("9q2JHc-P0LzNE-xzvY9j-vMoef2", false))
assert.True(t, IsAppPassword(AppPassword(), false))
assert.True(t, IsAppPassword(AppPassword(), false))
assert.False(t, IsAppPassword(AuthToken(), false))
assert.False(t, IsAppPassword(AuthToken(), false))
assert.False(t, IsAppPassword(SessionID(AuthToken()), false))
assert.False(t, IsAppPassword(SessionID(AuthToken()), false))
assert.False(t, IsAppPassword("55785BAC-9H4B-4747-B090-EE123FFEE437", false))
assert.False(t, IsAppPassword("4B1FEF2D1CF4A5BE38B263E0637EDEAD", false))
assert.False(t, IsAppPassword("69be27ac5ca305b394046a83f6fda18167ca3d3f2dbe7ac2", false))
assert.False(t, IsAppPassword("", false))
})
}
func BenchmarkIsAuthSecretVerifyChecksum(b *testing.B) {
func BenchmarkAppPasswordtVerifyChecksum(b *testing.B) {
for n := 0; n < b.N; n++ {
IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", true)
IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", true)
}
}
func BenchmarkIsAuthSecretIgnoreChecksum(b *testing.B) {
func BenchmarkAppPasswordIgnoreChecksum(b *testing.B) {
for n := 0; n < b.N; n++ {
IsAuthSecret("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", false)
IsAppPassword("MPkOqm-RtKGOi-ctIvXm-Qv3XhN", false)
}
}
func TestIsAuthAny(t *testing.T) {
assert.True(t, IsAuthAny("MPkOqm-RtKGOi-ctIvXm-Qv3XhN"))
assert.True(t, IsAuthAny("9q2JHc-P0LzNE-xzvY9j-vMoefj"))
assert.True(t, IsAuthAny(AuthSecret()))
assert.True(t, IsAuthAny(AuthSecret()))
assert.True(t, IsAuthAny(AppPassword()))
assert.True(t, IsAuthAny(AppPassword()))
assert.True(t, IsAuthAny(AuthToken()))
assert.True(t, IsAuthAny(AuthToken()))
assert.False(t, IsAuthAny(SessionID(AuthToken())))