mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 06:05:43 +00:00 
			
		
		
		
	feat: add support for required oauth groups
This commit is contained in:
		| @@ -42,6 +42,7 @@ | |||||||
|     "unauthorizedTitle": "Unauthorized", |     "unauthorizedTitle": "Unauthorized", | ||||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", |     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", |     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||||
|  |     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||||
|     "unauthorizedButton": "Try again", |     "unauthorizedButton": "Try again", | ||||||
|     "untrustedRedirectTitle": "Untrusted redirect", |     "untrustedRedirectTitle": "Untrusted redirect", | ||||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", |     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ | |||||||
|     "unauthorizedTitle": "Unauthorized", |     "unauthorizedTitle": "Unauthorized", | ||||||
|     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", |     "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.", | ||||||
|     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", |     "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.", | ||||||
|  |     "unauthorizedGroupsSubtitle": "The user with username <Code>{{username}}</Code> is not in the groups required by the resource <Code>{{resource}}</Code>.", | ||||||
|     "unauthorizedButton": "Try again", |     "unauthorizedButton": "Try again", | ||||||
|     "untrustedRedirectTitle": "Untrusted redirect", |     "untrustedRedirectTitle": "Untrusted redirect", | ||||||
|     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", |     "untrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain (<Code>{{domain}}</Code>). Are you sure you want to continue?", | ||||||
|   | |||||||
| @@ -3,11 +3,13 @@ import { Layout } from "../components/layouts/layout"; | |||||||
| import { Navigate } from "react-router"; | import { Navigate } from "react-router"; | ||||||
| import { isQueryValid } from "../utils/utils"; | import { isQueryValid } from "../utils/utils"; | ||||||
| import { Trans, useTranslation } from "react-i18next"; | import { Trans, useTranslation } from "react-i18next"; | ||||||
|  | import React from "react"; | ||||||
|  |  | ||||||
| export const UnauthorizedPage = () => { | export const UnauthorizedPage = () => { | ||||||
|   const queryString = window.location.search; |   const queryString = window.location.search; | ||||||
|   const params = new URLSearchParams(queryString); |   const params = new URLSearchParams(queryString); | ||||||
|   const username = params.get("username") ?? ""; |   const username = params.get("username") ?? ""; | ||||||
|  |   const groupErr = params.get("groupErr") ?? ""; | ||||||
|   const resource = params.get("resource") ?? ""; |   const resource = params.get("resource") ?? ""; | ||||||
|  |  | ||||||
|   const { t } = useTranslation(); |   const { t } = useTranslation(); | ||||||
| @@ -16,6 +18,47 @@ export const UnauthorizedPage = () => { | |||||||
|     return <Navigate to="/" />; |     return <Navigate to="/" />; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   if (isQueryValid(resource) && !isQueryValid(groupErr)) { | ||||||
|  |     return ( | ||||||
|  |       <UnauthorizedLayout> | ||||||
|  |           <Trans | ||||||
|  |             i18nKey="unauthorizedResourceSubtitle" | ||||||
|  |             t={t} | ||||||
|  |             components={{ Code: <Code /> }} | ||||||
|  |             values={{ resource, username }} | ||||||
|  |           /> | ||||||
|  |       </UnauthorizedLayout> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (isQueryValid(groupErr) && isQueryValid(resource)) { | ||||||
|  |     return ( | ||||||
|  |       <UnauthorizedLayout> | ||||||
|  |         <Trans | ||||||
|  |         i18nKey="unauthorizedGroupsSubtitle" | ||||||
|  |         t={t} | ||||||
|  |         components={{ Code: <Code /> }} | ||||||
|  |         values={{ username, resource }} | ||||||
|  |          /> | ||||||
|  |       </UnauthorizedLayout> | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <UnauthorizedLayout> | ||||||
|  |       <Trans | ||||||
|  |         i18nKey="unaothorizedLoginSubtitle" | ||||||
|  |         t={t} | ||||||
|  |         components={{ Code: <Code /> }} | ||||||
|  |         values={{ username }} | ||||||
|  |       /> | ||||||
|  |     </UnauthorizedLayout> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | const UnauthorizedLayout = ({ children }: { children: React.ReactNode }) => { | ||||||
|  |   const { t } = useTranslation(); | ||||||
|  |  | ||||||
|   return ( |   return ( | ||||||
|     <Layout> |     <Layout> | ||||||
|       <Paper shadow="md" p={30} mt={30} radius="md" withBorder> |       <Paper shadow="md" p={30} mt={30} radius="md" withBorder> | ||||||
| @@ -23,25 +66,7 @@ export const UnauthorizedPage = () => { | |||||||
|           {t("Unauthorized")} |           {t("Unauthorized")} | ||||||
|         </Text> |         </Text> | ||||||
|         <Text> |         <Text> | ||||||
|           {isQueryValid(resource) ? ( |         {children} | ||||||
|             <Text> |  | ||||||
|               <Trans |  | ||||||
|                 i18nKey="unauthorizedResourceSubtitle" |  | ||||||
|                 t={t} |  | ||||||
|                 components={{ Code: <Code /> }} |  | ||||||
|                 values={{ resource, username }} |  | ||||||
|               /> |  | ||||||
|             </Text> |  | ||||||
|           ) : ( |  | ||||||
|             <Text> |  | ||||||
|               <Trans |  | ||||||
|                 i18nKey="unaothorizedLoginSubtitle" |  | ||||||
|                 t={t} |  | ||||||
|                 components={{ Code: <Code /> }} |  | ||||||
|                 values={{ username }} |  | ||||||
|               /> |  | ||||||
|             </Text> |  | ||||||
|           )} |  | ||||||
|         </Text> |         </Text> | ||||||
|         <Button |         <Button | ||||||
|           fullWidth |           fullWidth | ||||||
|   | |||||||
| @@ -165,6 +165,7 @@ func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) | |||||||
| 	session.Values["provider"] = data.Provider | 	session.Values["provider"] = data.Provider | ||||||
| 	session.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix() | 	session.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix() | ||||||
| 	session.Values["totpPending"] = data.TotpPending | 	session.Values["totpPending"] = data.TotpPending | ||||||
|  | 	session.Values["oauthGroups"] = data.OAuthGroups | ||||||
|  |  | ||||||
| 	// Save session | 	// Save session | ||||||
| 	err = session.Save(c.Request, c.Writer) | 	err = session.Save(c.Request, c.Writer) | ||||||
| @@ -213,7 +214,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| 		return types.SessionCookie{}, err | 		return types.SessionCookie{}, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("session", session).Msg("Got session") | 	log.Debug().Msg("Got session") | ||||||
|  |  | ||||||
| 	// Get data from session | 	// Get data from session | ||||||
| 	username, usernameOk := session.Values["username"].(string) | 	username, usernameOk := session.Values["username"].(string) | ||||||
| @@ -222,8 +223,9 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| 	provider, providerOK := session.Values["provider"].(string) | 	provider, providerOK := session.Values["provider"].(string) | ||||||
| 	expiry, expiryOk := session.Values["expiry"].(int64) | 	expiry, expiryOk := session.Values["expiry"].(int64) | ||||||
| 	totpPending, totpPendingOk := session.Values["totpPending"].(bool) | 	totpPending, totpPendingOk := session.Values["totpPending"].(bool) | ||||||
|  | 	oauthGroups, oauthGroupsOk := session.Values["oauthGroups"].(string) | ||||||
|  |  | ||||||
| 	if !usernameOk || !providerOK || !expiryOk || !totpPendingOk || !emailOk || !nameOk { | 	if !usernameOk || !providerOK || !expiryOk || !totpPendingOk || !emailOk || !nameOk || !oauthGroupsOk { | ||||||
| 		log.Warn().Msg("Session cookie is invalid") | 		log.Warn().Msg("Session cookie is invalid") | ||||||
|  |  | ||||||
| 		// If any data is missing, delete the session cookie | 		// If any data is missing, delete the session cookie | ||||||
| @@ -244,7 +246,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| 		return types.SessionCookie{}, nil | 		return types.SessionCookie{}, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Str("name", name).Str("email", email).Msg("Parsed cookie") | 	log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Str("name", name).Str("email", email).Str("oauthGroups", oauthGroups).Msg("Parsed cookie") | ||||||
|  |  | ||||||
| 	// Return the cookie | 	// Return the cookie | ||||||
| 	return types.SessionCookie{ | 	return types.SessionCookie{ | ||||||
| @@ -253,6 +255,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| 		Email:       email, | 		Email:       email, | ||||||
| 		Provider:    provider, | 		Provider:    provider, | ||||||
| 		TotpPending: totpPending, | 		TotpPending: totpPending, | ||||||
|  | 		OAuthGroups: oauthGroups, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -261,48 +264,46 @@ func (auth *Auth) UserAuthConfigured() bool { | |||||||
| 	return len(auth.Config.Users) > 0 | 	return len(auth.Config.Users) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bool, error) { | func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext, labels types.TinyauthLabels) bool { | ||||||
| 	// Get headers |  | ||||||
| 	host := c.Request.Header.Get("X-Forwarded-Host") |  | ||||||
|  |  | ||||||
| 	// Get app id |  | ||||||
| 	appId := strings.Split(host, ".")[0] |  | ||||||
|  |  | ||||||
| 	// Get the container labels |  | ||||||
| 	labels, err := auth.Docker.GetLabels(appId) |  | ||||||
|  |  | ||||||
| 	// If there is an error, return false |  | ||||||
| 	if err != nil { |  | ||||||
| 		return false, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Check if oauth is allowed | 	// Check if oauth is allowed | ||||||
| 	if context.OAuth { | 	if context.OAuth { | ||||||
| 		log.Debug().Msg("Checking OAuth whitelist") | 		log.Debug().Msg("Checking OAuth whitelist") | ||||||
| 		return utils.CheckWhitelist(labels.OAuthWhitelist, context.Username), nil | 		return utils.CheckWhitelist(labels.OAuthWhitelist, context.Username) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Check users | 	// Check users | ||||||
| 	log.Debug().Msg("Checking users") | 	log.Debug().Msg("Checking users") | ||||||
|  |  | ||||||
| 	return utils.CheckWhitelist(labels.Users, context.Username), nil | 	return utils.CheckWhitelist(labels.Users, context.Username) | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) AuthEnabled(c *gin.Context) (bool, error) { | func (auth *Auth) OAuthGroup(c *gin.Context, context types.UserContext, labels types.TinyauthLabels) bool { | ||||||
|  | 	// Check if groups are required | ||||||
|  | 	if labels.OAuthGroups == "" { | ||||||
|  | 		return true | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Split the groups by comma (no need to parse since they are from the API response) | ||||||
|  | 	oauthGroups := strings.Split(context.OAuthGroups, ",") | ||||||
|  |  | ||||||
|  | 	// For every group check if it is in the required groups | ||||||
|  | 	for _, group := range oauthGroups { | ||||||
|  | 		if utils.CheckWhitelist(labels.OAuthGroups, group) { | ||||||
|  | 			log.Debug().Str("group", group).Msg("Group is in required groups") | ||||||
|  | 			return true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// No groups matched | ||||||
|  | 	log.Debug().Msg("No groups matched") | ||||||
|  |  | ||||||
|  | 	// Return false | ||||||
|  | 	return false | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (auth *Auth) AuthEnabled(c *gin.Context, labels types.TinyauthLabels) (bool, error) { | ||||||
| 	// Get headers | 	// Get headers | ||||||
| 	uri := c.Request.Header.Get("X-Forwarded-Uri") | 	uri := c.Request.Header.Get("X-Forwarded-Uri") | ||||||
| 	host := c.Request.Header.Get("X-Forwarded-Host") |  | ||||||
|  |  | ||||||
| 	// Get app id |  | ||||||
| 	appId := strings.Split(host, ".")[0] |  | ||||||
|  |  | ||||||
| 	// Get the container labels |  | ||||||
| 	labels, err := auth.Docker.GetLabels(appId) |  | ||||||
|  |  | ||||||
| 	// If there is an error, auth enabled |  | ||||||
| 	if err != nil { |  | ||||||
| 		return true, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Check if the allowed label is empty | 	// Check if the allowed label is empty | ||||||
| 	if labels.Allowed == "" { | 	if labels.Allowed == "" { | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ var TinyauthLabels = []string{ | |||||||
| 	"tinyauth.users", | 	"tinyauth.users", | ||||||
| 	"tinyauth.allowed", | 	"tinyauth.allowed", | ||||||
| 	"tinyauth.headers", | 	"tinyauth.headers", | ||||||
|  | 	"tinyauth.oauth.groups", | ||||||
| } | } | ||||||
|  |  | ||||||
| // Claims are the OIDC supported claims (including preferd username for some reason) | // Claims are the OIDC supported claims (including preferd username for some reason) | ||||||
| @@ -13,4 +14,5 @@ type Claims struct { | |||||||
| 	Name              string   `json:"name"` | 	Name              string   `json:"name"` | ||||||
| 	Email             string   `json:"email"` | 	Email             string   `json:"email"` | ||||||
| 	PreferredUsername string   `json:"preferred_username"` | 	PreferredUsername string   `json:"preferred_username"` | ||||||
|  | 	Groups            []string `json:"groups"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -69,12 +69,15 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | |||||||
| 	proto := c.Request.Header.Get("X-Forwarded-Proto") | 	proto := c.Request.Header.Get("X-Forwarded-Proto") | ||||||
| 	host := c.Request.Header.Get("X-Forwarded-Host") | 	host := c.Request.Header.Get("X-Forwarded-Host") | ||||||
|  |  | ||||||
| 	// Check if auth is enabled | 	// Get the app id | ||||||
| 	authEnabled, err := h.Auth.AuthEnabled(c) | 	appId := strings.Split(host, ".")[0] | ||||||
|  |  | ||||||
|  | 	// Get the container labels | ||||||
|  | 	labels, err := h.Docker.GetLabels(appId) | ||||||
|  |  | ||||||
| 	// Check if there was an error | 	// Check if there was an error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		log.Error().Err(err).Msg("Failed to check if app is allowed") | 		log.Error().Err(err).Msg("Failed to get container labels") | ||||||
|  |  | ||||||
| 		if proxy.Proxy == "nginx" || !isBrowser { | 		if proxy.Proxy == "nginx" || !isBrowser { | ||||||
| 			c.JSON(500, gin.H{ | 			c.JSON(500, gin.H{ | ||||||
| @@ -88,11 +91,8 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Get the app id | 	// Check if auth is enabled | ||||||
| 	appId := strings.Split(host, ".")[0] | 	authEnabled, err := h.Auth.AuthEnabled(c, labels) | ||||||
|  |  | ||||||
| 	// Get the container labels |  | ||||||
| 	labels, err := h.Docker.GetLabels(appId) |  | ||||||
|  |  | ||||||
| 	// Check if there was an error | 	// Check if there was an error | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| @@ -131,23 +131,7 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | |||||||
| 		log.Debug().Msg("Authenticated") | 		log.Debug().Msg("Authenticated") | ||||||
|  |  | ||||||
| 		// Check if user is allowed to access subdomain, if request is nginx.example.com the subdomain (resource) is nginx | 		// Check if user is allowed to access subdomain, if request is nginx.example.com the subdomain (resource) is nginx | ||||||
| 		appAllowed, err := h.Auth.ResourceAllowed(c, userContext) | 		appAllowed := h.Auth.ResourceAllowed(c, userContext, labels) | ||||||
|  |  | ||||||
| 		// Check if there was an error |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error().Err(err).Msg("Failed to check if app is allowed") |  | ||||||
|  |  | ||||||
| 			if proxy.Proxy == "nginx" || !isBrowser { |  | ||||||
| 				c.JSON(500, gin.H{ |  | ||||||
| 					"status":  500, |  | ||||||
| 					"message": "Internal Server Error", |  | ||||||
| 				}) |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
|  |  | ||||||
| 			c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL)) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		log.Debug().Bool("appAllowed", appAllowed).Msg("Checking if app is allowed") | 		log.Debug().Bool("appAllowed", appAllowed).Msg("Checking if app is allowed") | ||||||
|  |  | ||||||
| @@ -184,9 +168,51 @@ func (h *Handlers) AuthHandler(c *gin.Context) { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		log.Debug().Interface("labels", labels).Msg("Got labels") | ||||||
|  |  | ||||||
|  | 		// Check if user is in required groups | ||||||
|  | 		groupOk := h.Auth.OAuthGroup(c, userContext, labels) | ||||||
|  |  | ||||||
|  | 		log.Debug().Bool("groupOk", groupOk).Msg("Checking if user is in required groups") | ||||||
|  |  | ||||||
|  | 		// The user is not allowed to access the app | ||||||
|  | 		if !groupOk { | ||||||
|  | 			log.Warn().Str("username", userContext.Username).Str("host", host).Msg("User is not in required groups") | ||||||
|  |  | ||||||
|  | 			// Set WWW-Authenticate header | ||||||
|  | 			c.Header("WWW-Authenticate", "Basic realm=\"tinyauth\"") | ||||||
|  |  | ||||||
|  | 			if proxy.Proxy == "nginx" || !isBrowser { | ||||||
|  | 				c.JSON(401, gin.H{ | ||||||
|  | 					"status":  401, | ||||||
|  | 					"message": "Unauthorized", | ||||||
|  | 				}) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// Build query | ||||||
|  | 			queries, err := query.Values(types.UnauthorizedQuery{ | ||||||
|  | 				Username: userContext.Username, | ||||||
|  | 				Resource: strings.Split(host, ".")[0], | ||||||
|  | 				GroupErr: true, | ||||||
|  | 			}) | ||||||
|  |  | ||||||
|  | 			// Handle error (no need to check for nginx/headers since we are sure we are using caddy/traefik) | ||||||
|  | 			if err != nil { | ||||||
|  | 				log.Error().Err(err).Msg("Failed to build queries") | ||||||
|  | 				c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("%s/error", h.Config.AppURL)) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			// We are using caddy/traefik so redirect | ||||||
|  | 			c.Redirect(http.StatusTemporaryRedirect, fmt.Sprintf("%s/unauthorized?%s", h.Config.AppURL, queries.Encode())) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		c.Header("Remote-User", userContext.Username) | 		c.Header("Remote-User", userContext.Username) | ||||||
| 		c.Header("Remote-Name", userContext.Name) | 		c.Header("Remote-Name", userContext.Name) | ||||||
| 		c.Header("Remote-Email", userContext.Email) | 		c.Header("Remote-Email", userContext.Email) | ||||||
|  | 		c.Header("Remote-Groups", userContext.OAuthGroups) | ||||||
|  |  | ||||||
| 		// Set the rest of the headers | 		// Set the rest of the headers | ||||||
| 		for key, value := range labels.Headers { | 		for key, value := range labels.Headers { | ||||||
| @@ -688,6 +714,7 @@ func (h *Handlers) OauthCallbackHandler(c *gin.Context) { | |||||||
| 		Name:        name, | 		Name:        name, | ||||||
| 		Email:       user.Email, | 		Email:       user.Email, | ||||||
| 		Provider:    providerName.Provider, | 		Provider:    providerName.Provider, | ||||||
|  | 		OAuthGroups: strings.Join(user.Groups, ","), | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
| 	// Check if we have a redirect URI | 	// Check if we have a redirect URI | ||||||
|   | |||||||
| @@ -120,6 +120,7 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | |||||||
| 			IsLoggedIn:  true, | 			IsLoggedIn:  true, | ||||||
| 			OAuth:       true, | 			OAuth:       true, | ||||||
| 			Provider:    cookie.Provider, | 			Provider:    cookie.Provider, | ||||||
|  | 			OAuthGroups: cookie.OAuthGroups, | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -20,6 +20,7 @@ type OAuthRequest struct { | |||||||
| type UnauthorizedQuery struct { | type UnauthorizedQuery struct { | ||||||
| 	Username string `url:"username"` | 	Username string `url:"username"` | ||||||
| 	Resource string `url:"resource"` | 	Resource string `url:"resource"` | ||||||
|  | 	GroupErr bool   `url:"groupErr"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // Proxy is the uri parameters for the proxy endpoint | // Proxy is the uri parameters for the proxy endpoint | ||||||
|   | |||||||
| @@ -29,6 +29,7 @@ type SessionCookie struct { | |||||||
| 	Email       string | 	Email       string | ||||||
| 	Provider    string | 	Provider    string | ||||||
| 	TotpPending bool | 	TotpPending bool | ||||||
|  | 	OAuthGroups string | ||||||
| } | } | ||||||
|  |  | ||||||
| // TinyauthLabels is the labels for the tinyauth container | // TinyauthLabels is the labels for the tinyauth container | ||||||
| @@ -37,6 +38,7 @@ type TinyauthLabels struct { | |||||||
| 	Users          string | 	Users          string | ||||||
| 	Allowed        string | 	Allowed        string | ||||||
| 	Headers        map[string]string | 	Headers        map[string]string | ||||||
|  | 	OAuthGroups    string | ||||||
| } | } | ||||||
|  |  | ||||||
| // UserContext is the context for the user | // UserContext is the context for the user | ||||||
| @@ -48,6 +50,7 @@ type UserContext struct { | |||||||
| 	OAuth       bool | 	OAuth       bool | ||||||
| 	Provider    string | 	Provider    string | ||||||
| 	TotpPending bool | 	TotpPending bool | ||||||
|  | 	OAuthGroups string | ||||||
| } | } | ||||||
|  |  | ||||||
| // LoginAttempt tracks information about login attempts for rate limiting | // LoginAttempt tracks information about login attempts for rate limiting | ||||||
|   | |||||||
| @@ -204,6 +204,8 @@ func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels { | |||||||
| 					} | 					} | ||||||
| 					tinyauthLabels.Headers[headerSplit[0]] = headerSplit[1] | 					tinyauthLabels.Headers[headerSplit[0]] = headerSplit[1] | ||||||
| 				} | 				} | ||||||
|  | 			case "tinyauth.oauth.groups": | ||||||
|  | 				tinyauthLabels.OAuthGroups = value | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Stavros
					Stavros