feat: use org name as TOTP issuer (#4731)

This commit is contained in:
DacongDA
2025-12-29 13:49:01 +08:00
committed by GitHub
parent b46b79ee44
commit db594e2096
8 changed files with 133 additions and 12 deletions

View File

@@ -64,7 +64,14 @@ func (c *ApiController) MfaSetupInitiate() {
return
}
mfaProps, err := MfaUtil.Initiate(user.GetId())
issuer := ""
if organization != nil && organization.DisplayName != "" {
issuer = organization.DisplayName
} else if organization != nil {
issuer = organization.Name
}
mfaProps, err := MfaUtil.Initiate(user.GetId(), issuer)
if err != nil {
c.ResponseError(err.Error())
return

View File

@@ -32,7 +32,7 @@ type MfaProps struct {
}
type MfaInterface interface {
Initiate(userId string) (*MfaProps, error)
Initiate(userId string, issuer string) (*MfaProps, error)
SetupVerify(passcode string) error
Enable(user *User) error
Verify(passcode string) error

View File

@@ -31,7 +31,7 @@ type PushMfa struct {
challengeExp time.Time
}
func (mfa *PushMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *PushMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -29,7 +29,7 @@ type RadiusMfa struct {
provider *Provider
}
func (mfa *RadiusMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *RadiusMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -35,7 +35,7 @@ func TestRadiusMfaUtil(t *testing.T) {
}
// Test Initiate
mfaProps, err := radiusMfa.Initiate("test/user")
mfaProps, err := radiusMfa.Initiate("test/user", "")
if err != nil {
t.Errorf("Initiate failed: %v", err)
}

View File

@@ -24,7 +24,7 @@ type SmsMfa struct {
*MfaProps
}
func (mfa *SmsMfa) Initiate(userId string) (*MfaProps, error) {
func (mfa *SmsMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
mfaProps := MfaProps{
MfaType: mfa.MfaType,
}

View File

@@ -33,12 +33,11 @@ type TotpMfa struct {
digits otp.Digits
}
func (mfa *TotpMfa) Initiate(userId string) (*MfaProps, error) {
//issuer := beego.AppConfig.String("appname")
//if issuer == "" {
// issuer = "casdoor"
//}
issuer := "Casdoor"
func (mfa *TotpMfa) Initiate(userId string, issuer string) (*MfaProps, error) {
// Use the provided issuer (application display name), or fall back to "Casdoor" if not provided
if issuer == "" {
issuer = "Casdoor"
}
key, err := totp.Generate(totp.GenerateOpts{
Issuer: issuer,

115
object/mfa_totp_test.go Normal file
View File

@@ -0,0 +1,115 @@
// Copyright 2025 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"strings"
"testing"
)
func TestTotpMfaUtil(t *testing.T) {
// Test creating a new TOTP MFA util
config := &MfaProps{
MfaType: TotpType,
}
totpMfa := NewTotpMfaUtil(config)
if totpMfa == nil {
t.Error("NewTotpMfaUtil returned nil")
return
}
if totpMfa.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, totpMfa.MfaType)
}
}
func TestTotpMfaInitiate_WithCustomIssuer(t *testing.T) {
totpMfa := NewTotpMfaUtil(nil)
// Test with custom issuer (application display name)
customIssuer := "My Application"
mfaProps, err := totpMfa.Initiate("test/user", customIssuer)
if err != nil {
t.Errorf("Initiate failed: %v", err)
}
if mfaProps == nil {
t.Error("Initiate returned nil mfaProps")
return
}
if mfaProps.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, mfaProps.MfaType)
}
if mfaProps.Secret == "" {
t.Error("Secret should not be empty")
}
if mfaProps.URL == "" {
t.Error("URL should not be empty")
}
// Verify the URL contains the custom issuer (URL-encoded or plain)
if !strings.Contains(mfaProps.URL, customIssuer) && !strings.Contains(mfaProps.URL, "My%20Application") {
t.Errorf("URL should contain custom issuer '%s', got: %s", customIssuer, mfaProps.URL)
}
// Verify the URL contains the user ID
if !strings.Contains(mfaProps.URL, "test/user") {
t.Errorf("URL should contain user ID 'test/user', got: %s", mfaProps.URL)
}
}
func TestTotpMfaInitiate_WithEmptyIssuer(t *testing.T) {
totpMfa := NewTotpMfaUtil(nil)
// Test with empty issuer (should default to "Casdoor")
mfaProps, err := totpMfa.Initiate("test/user", "")
if err != nil {
t.Errorf("Initiate failed: %v", err)
}
if mfaProps == nil {
t.Error("Initiate returned nil mfaProps")
return
}
// Verify the URL contains the default issuer "Casdoor"
if !strings.Contains(mfaProps.URL, "Casdoor") {
t.Errorf("URL should contain default issuer 'Casdoor', got: %s", mfaProps.URL)
}
}
func TestGetMfaUtil_Totp(t *testing.T) {
config := &MfaProps{
MfaType: TotpType,
Secret: "testsecret",
}
mfaUtil := GetMfaUtil(TotpType, config)
if mfaUtil == nil {
t.Error("GetMfaUtil returned nil for TOTP type")
return
}
totpMfa, ok := mfaUtil.(*TotpMfa)
if !ok {
t.Error("GetMfaUtil did not return TotpMfa type")
}
if totpMfa.MfaType != TotpType {
t.Errorf("Expected MFA type %s, got %s", TotpType, totpMfa.MfaType)
}
}