mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-05-10 06:18:11 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f8fb7d678b | |||
| 0c31fb0600 | |||
| 6b5a6bd982 |
@@ -49,11 +49,6 @@ jobs:
|
||||
run: |
|
||||
cp -r frontend/dist internal/assets/dist
|
||||
|
||||
- name: Lint backend
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: v2.12
|
||||
|
||||
- name: Run tests
|
||||
run: go test -coverprofile=coverage.txt -v ./...
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
version: "2"
|
||||
linters:
|
||||
settings:
|
||||
errcheck:
|
||||
exclude-functions:
|
||||
- (http.ResponseWriter).Write
|
||||
- (http.ResponseWriter).WriteString
|
||||
- (github.com/gin-gonic/gin.ResponseWriter).Write
|
||||
- (github.com/gin-gonic/gin.ResponseWriter).WriteString
|
||||
exclusions:
|
||||
rules:
|
||||
- linters:
|
||||
- errcheck
|
||||
text: "//nolint:errcheck"
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "//nolint:staticcheck"
|
||||
@@ -65,7 +65,7 @@ Tinyauth is licensed under the GNU General Public License v3.0. TL;DR — You ma
|
||||
|
||||
A big thank you to the following people for providing me with more coffee:
|
||||
|
||||
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https://github.com/erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a> <a href="https://github.com/nicotsx"><img src="https://github.com/nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a> <a href="https://github.com/SimpleHomelab"><img src="https://github.com/SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a> <a href="https://github.com/jmadden91"><img src="https://github.com/jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a> <a href="https://github.com/tribor"><img src="https://github.com/tribor.png" width="64px" alt="User avatar: tribor" /></a> <a href="https://github.com/eliasbenb"><img src="https://github.com/eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a> <a href="https://github.com/afunworm"><img src="https://github.com/afunworm.png" width="64px" alt="User avatar: afunworm" /></a> <a href="https://github.com/chip-well"><img src="https://github.com/chip-well.png" width="64px" alt="User avatar: chip-well" /></a> <a href="https://github.com/Lancelot-Enguerrand"><img src="https://github.com/Lancelot-Enguerrand.png" width="64px" alt="User avatar: Lancelot-Enguerrand" /></a> <a href="https://github.com/allgoewer"><img src="https://github.com/allgoewer.png" width="64px" alt="User avatar: allgoewer" /></a> <a href="https://github.com/NEANC"><img src="https://github.com/NEANC.png" width="64px" alt="User avatar: NEANC" /></a> <a href="https://github.com/ax-mad"><img src="https://github.com/ax-mad.png" width="64px" alt="User avatar: ax-mad" /></a> <a href="https://github.com/stegratech"><img src="https://github.com/stegratech.png" width="64px" alt="User avatar: stegratech" /></a> <a href="https://github.com/apearson"><img src="https://github.com/apearson.png" width="64px" alt="User avatar: apearson" /></a> <!-- sponsors -->
|
||||
<!-- sponsors --><a href="https://github.com/erwinkramer"><img src="https://github.com/erwinkramer.png" width="64px" alt="User avatar: erwinkramer" /></a> <a href="https://github.com/nicotsx"><img src="https://github.com/nicotsx.png" width="64px" alt="User avatar: nicotsx" /></a> <a href="https://github.com/SimpleHomelab"><img src="https://github.com/SimpleHomelab.png" width="64px" alt="User avatar: SimpleHomelab" /></a> <a href="https://github.com/jmadden91"><img src="https://github.com/jmadden91.png" width="64px" alt="User avatar: jmadden91" /></a> <a href="https://github.com/tribor"><img src="https://github.com/tribor.png" width="64px" alt="User avatar: tribor" /></a> <a href="https://github.com/eliasbenb"><img src="https://github.com/eliasbenb.png" width="64px" alt="User avatar: eliasbenb" /></a> <a href="https://github.com/afunworm"><img src="https://github.com/afunworm.png" width="64px" alt="User avatar: afunworm" /></a> <a href="https://github.com/chip-well"><img src="https://github.com/chip-well.png" width="64px" alt="User avatar: chip-well" /></a> <a href="https://github.com/Lancelot-Enguerrand"><img src="https://github.com/Lancelot-Enguerrand.png" width="64px" alt="User avatar: Lancelot-Enguerrand" /></a> <a href="https://github.com/allgoewer"><img src="https://github.com/allgoewer.png" width="64px" alt="User avatar: allgoewer" /></a> <a href="https://github.com/NEANC"><img src="https://github.com/NEANC.png" width="64px" alt="User avatar: NEANC" /></a> <a href="https://github.com/ax-mad"><img src="https://github.com/ax-mad.png" width="64px" alt="User avatar: ax-mad" /></a> <a href="https://github.com/stegratech"><img src="https://github.com/stegratech.png" width="64px" alt="User avatar: stegratech" /></a> <!-- sponsors -->
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
|
||||
@@ -68,7 +68,10 @@ func generateTotpCmd() *cli.Command {
|
||||
return fmt.Errorf("failed to parse user: %w", err)
|
||||
}
|
||||
|
||||
docker := strings.Contains(tCfg.User, "$$")
|
||||
docker := false
|
||||
if strings.Contains(tCfg.User, "$$") {
|
||||
docker = true
|
||||
}
|
||||
|
||||
if user.TOTPSecret != "" {
|
||||
return fmt.Errorf("user already has a TOTP secret")
|
||||
|
||||
@@ -9,8 +9,8 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/tinyauthapp/paerser/cli"
|
||||
"github.com/tinyauthapp/tinyauth/internal/utils/tlog"
|
||||
"github.com/tinyauthapp/paerser/cli"
|
||||
)
|
||||
|
||||
type healthzResponse struct {
|
||||
@@ -45,7 +45,7 @@ func healthcheckCmd() *cli.Command {
|
||||
}
|
||||
|
||||
if appUrl == "" {
|
||||
return errors.New("could not determine app url")
|
||||
return errors.New("Could not determine app URL")
|
||||
}
|
||||
|
||||
tlog.App.Info().Str("app_url", appUrl).Msg("Performing health check")
|
||||
@@ -70,7 +70,7 @@ func healthcheckCmd() *cli.Command {
|
||||
return fmt.Errorf("service is not healthy, got: %s", resp.Status)
|
||||
}
|
||||
|
||||
defer resp.Body.Close() //nolint:errcheck
|
||||
defer resp.Body.Close()
|
||||
|
||||
var healthResp healthzResponse
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ func (app *BootstrapApp) heartbeatRoutine() {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Body.Close() //nolint:errcheck
|
||||
res.Body.Close()
|
||||
|
||||
if res.StatusCode != 200 && res.StatusCode != 201 {
|
||||
tlog.App.Debug().Str("status", res.Status).Msg("Heartbeat returned non-200/201 status")
|
||||
|
||||
@@ -36,7 +36,7 @@ func (app *BootstrapApp) initServices(queries *repository.Queries) (Services, er
|
||||
|
||||
if err != nil {
|
||||
tlog.App.Warn().Err(err).Msg("Failed to setup LDAP service, starting without it")
|
||||
ldapService.Unconfigure() //nolint:errcheck
|
||||
ldapService.Unconfigure()
|
||||
}
|
||||
|
||||
services.ldapService = ldapService
|
||||
|
||||
@@ -166,12 +166,6 @@ func (controller *OAuthController) oauthCallbackHandler(c *gin.Context) {
|
||||
|
||||
user, err := controller.auth.GetOAuthUserinfo(sessionIdCookie)
|
||||
|
||||
if err != nil {
|
||||
tlog.App.Error().Err(err).Msg("Failed to get user info from OAuth provider")
|
||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
|
||||
return
|
||||
}
|
||||
|
||||
if user.Email == "" {
|
||||
tlog.App.Error().Msg("OAuth provider did not return an email")
|
||||
c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/error", controller.config.AppURL))
|
||||
|
||||
@@ -179,7 +179,7 @@ func TestUserController(t *testing.T) {
|
||||
{
|
||||
description: "Should rate limit on 3 invalid attempts",
|
||||
middlewares: []gin.HandlerFunc{},
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) { //nolint:staticcheck
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||
loginReq := controller.LoginRequest{
|
||||
Username: "testuser",
|
||||
Password: "wrongpassword",
|
||||
@@ -201,7 +201,7 @@ func TestUserController(t *testing.T) {
|
||||
}
|
||||
|
||||
// 4th attempt should be rate limited
|
||||
recorder = httptest.NewRecorder() //nolint:staticcheck
|
||||
recorder = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/api/user/login", strings.NewReader(string(loginReqBody)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
@@ -293,7 +293,7 @@ func TestUserController(t *testing.T) {
|
||||
middlewares: []gin.HandlerFunc{
|
||||
totpCtx,
|
||||
},
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) { //nolint:staticcheck
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||
_, err := queries.CreateSession(context.TODO(), repository.CreateSessionParams{
|
||||
UUID: "test-totp-login-uuid",
|
||||
Username: "test",
|
||||
@@ -316,7 +316,7 @@ func TestUserController(t *testing.T) {
|
||||
totpReqBody, err := json.Marshal(totpReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
recorder = httptest.NewRecorder() //nolint:staticcheck
|
||||
recorder = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqBody)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.AddCookie(&http.Cookie{
|
||||
@@ -345,7 +345,7 @@ func TestUserController(t *testing.T) {
|
||||
middlewares: []gin.HandlerFunc{
|
||||
totpCtx,
|
||||
},
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) { //nolint:staticcheck
|
||||
run: func(t *testing.T, router *gin.Engine, recorder *httptest.ResponseRecorder) {
|
||||
for range 3 {
|
||||
totpReq := controller.TotpRequest{
|
||||
Code: "000000", // invalid code
|
||||
@@ -354,7 +354,7 @@ func TestUserController(t *testing.T) {
|
||||
totpReqBody, err := json.Marshal(totpReq)
|
||||
assert.NoError(t, err)
|
||||
|
||||
recorder = httptest.NewRecorder() //nolint:staticcheck
|
||||
recorder = httptest.NewRecorder()
|
||||
req := httptest.NewRequest("POST", "/api/user/totp", strings.NewReader(string(totpReqBody)))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
|
||||
@@ -171,7 +171,7 @@ func (m *ContextMiddleware) cookieAuth(ctx context.Context, uuid string) (*model
|
||||
}
|
||||
|
||||
if !m.auth.IsEmailWhitelisted(userContext.OAuth.Email) {
|
||||
m.auth.DeleteSession(ctx, uuid) //nolint:errcheck
|
||||
m.auth.DeleteSession(ctx, uuid)
|
||||
return nil, nil, fmt.Errorf("email from session cookie not whitelisted: %s", userContext.OAuth.Email)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,3 +845,10 @@ func (auth *AuthService) ClearRateLimitsTestingOnly() {
|
||||
}
|
||||
auth.loginMutex.Unlock()
|
||||
}
|
||||
|
||||
func (auth *AuthService) getCookieDomain() string {
|
||||
if auth.config.SubdomainsEnabled {
|
||||
return "." + auth.config.CookieDomain
|
||||
}
|
||||
return auth.config.CookieDomain
|
||||
}
|
||||
|
||||
@@ -269,7 +269,7 @@ func (ldap *LdapService) reconnect() error {
|
||||
exp.Reset()
|
||||
|
||||
operation := func() (*ldapgo.Conn, error) {
|
||||
ldap.conn.Close() //nolint:errcheck
|
||||
ldap.conn.Close()
|
||||
conn, err := ldap.connect()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -92,7 +92,7 @@ func simpleReq[T any](client *http.Client, url string, headers map[string]string
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close() //nolint:errcheck
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode < 200 || res.StatusCode >= 300 {
|
||||
return nil, fmt.Errorf("request failed with status: %s", res.Status)
|
||||
|
||||
@@ -18,7 +18,7 @@ func TestReadFile(t *testing.T) {
|
||||
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
defer os.Remove("/tmp/tinyauth_test_file") //nolint:errcheck
|
||||
defer os.Remove("/tmp/tinyauth_test_file")
|
||||
|
||||
// Normal case
|
||||
content, err := ReadFile("/tmp/tinyauth_test_file")
|
||||
|
||||
@@ -53,7 +53,7 @@ func FilterIP(filter string, ip string) (bool, error) {
|
||||
return false, errors.New("invalid IP address")
|
||||
}
|
||||
|
||||
filter = strings.ReplaceAll(filter, "-", "/")
|
||||
filter = strings.Replace(filter, "-", "/", -1)
|
||||
|
||||
if strings.Contains(filter, "/") {
|
||||
_, cidr, err := net.ParseCIDR(filter)
|
||||
|
||||
@@ -19,7 +19,7 @@ func TestGetSecret(t *testing.T) {
|
||||
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
defer os.Remove("/tmp/tinyauth_test_secret") //nolint:errcheck
|
||||
defer os.Remove("/tmp/tinyauth_test_secret")
|
||||
|
||||
// Get from config
|
||||
assert.Equal(t, "mysecret", utils.GetSecret("mysecret", ""))
|
||||
|
||||
@@ -61,29 +61,29 @@ func TestCompileUserEmail(t *testing.T) {
|
||||
func TestParseNonEmptyLines(t *testing.T) {
|
||||
lines := utils.ParseNonEmptyLines(" first@example.com \n\n second@example.com \n \n")
|
||||
|
||||
assert.Equal(t, []string{"first@example.com", "second@example.com"}, lines)
|
||||
assert.DeepEqual(t, []string{"first@example.com", "second@example.com"}, lines)
|
||||
}
|
||||
|
||||
func TestGetStringList(t *testing.T) {
|
||||
file, err := os.Create("/tmp/tinyauth_list_test_file")
|
||||
assert.NoError(t, err)
|
||||
assert.NilError(t, err)
|
||||
|
||||
_, err = file.WriteString(" third@example.com \n\n fourth@example.com \n")
|
||||
assert.NoError(t, err)
|
||||
assert.NilError(t, err)
|
||||
|
||||
err = file.Close()
|
||||
assert.NoError(t, err)
|
||||
defer os.Remove("/tmp/tinyauth_list_test_file") //nolint:errcheck
|
||||
assert.NilError(t, err)
|
||||
defer os.Remove("/tmp/tinyauth_list_test_file")
|
||||
|
||||
values, err := utils.GetStringList([]string{" first@example.com ", "", "second@example.com"}, "/tmp/tinyauth_list_test_file")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"first@example.com", "second@example.com", "third@example.com", "fourth@example.com"}, values)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, []string{"first@example.com", "second@example.com", "third@example.com", "fourth@example.com"}, values)
|
||||
|
||||
values, err = utils.GetStringList(nil, "")
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{}, values)
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, []string{}, values)
|
||||
|
||||
values, err = utils.GetStringList(nil, "/tmp/non_existing_list_file")
|
||||
assert.ErrorContains(t, err, "no such file or directory")
|
||||
assert.Equal(t, []string{}, values)
|
||||
assert.DeepEqual(t, []string{}, values)
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ func ParseUsers(usersStr []string, userAttributes map[string]model.UserAttribute
|
||||
var users []model.LocalUser
|
||||
|
||||
if len(usersStr) == 0 {
|
||||
return nil, nil
|
||||
return &users, nil
|
||||
}
|
||||
|
||||
for _, user := range usersStr {
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestGetUsers(t *testing.T) {
|
||||
|
||||
err = file.Close()
|
||||
require.NoError(t, err)
|
||||
defer os.Remove(tmpDir + "/tinyauth_users_test.txt") //nolint:errcheck
|
||||
defer os.Remove(tmpDir + "/tinyauth_users_test.txt")
|
||||
|
||||
noAttrs := map[string]model.UserAttributes{}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user