diff --git a/server/conf.go b/server/conf.go index ef9afa7..1464379 100644 --- a/server/conf.go +++ b/server/conf.go @@ -11,6 +11,10 @@ type Conf struct { ServiceName string `json:"service_name"` Issuer string `json:"issuer"` SsoServices []issuer.SsoConfig `json:"sso_services"` - AllowedClients []utils.JsonUrl `json:"allowed_clients"` - Permissions []string `json:"permissions"` + AllowedClients []AllowedClient `json:"allowed_clients"` +} + +type AllowedClient struct { + Url utils.JsonUrl `json:"url"` + Permissions []string `json:"permissions"` } diff --git a/server/flow.go b/server/flow.go index f802853..8e9204d 100644 --- a/server/flow.go +++ b/server/flow.go @@ -51,7 +51,8 @@ func (h *HttpServer) flowPopupPost(rw http.ResponseWriter, req *http.Request, _ loginUn := loginName[:n] targetOrigin := req.PostFormValue("origin") - if _, found := h.services[targetOrigin]; !found { + allowedService, found := h.services[targetOrigin] + if !found { http.Error(rw, "Invalid target origin", http.StatusBadRequest) return } @@ -60,7 +61,7 @@ func (h *HttpServer) flowPopupPost(rw http.ResponseWriter, req *http.Request, _ state := login.Config.Namespace + ":" + uuidNewStringState() h.flowState.Set(state, flowStateData{ login, - targetOrigin, + allowedService, }, time.Now().Add(15*time.Minute)) // generate oauth2 config and redirect to authorize URL @@ -128,26 +129,36 @@ func (h *HttpServer) flowCallback(rw http.ResponseWriter, req *http.Request, _ h return } + var needsMailFlag bool + ps := claims.NewPermStorage() - for _, i := range h.conf.Permissions { - ps.Set(i) + for _, i := range v.target.Permissions { + if strings.HasPrefix(i, "dynamic:") { + if i == "dynamic:mail-client" { + needsMailFlag = true + } + } else { + ps.Set(i) + } } - if verified, ok := v3["email_verified"].(bool); ok && verified { - if mailAddress, ok := v3["email"].(string); ok { - address, err := mail.ParseAddress(mailAddress) - if err != nil { - http.Error(rw, "Invalid email in userinfo", http.StatusInternalServerError) - return + if needsMailFlag { + if verified, ok := v3["email_verified"].(bool); ok && verified { + if mailAddress, ok := v3["email"].(string); ok { + address, err := mail.ParseAddress(mailAddress) + if err != nil { + http.Error(rw, "Invalid email in userinfo", http.StatusInternalServerError) + return + } + n := strings.IndexByte(address.Address, '@') + if n == -1 { + goto noEmailSupport + } + if address.Address[n+1:] != v.sso.Config.Namespace { + goto noEmailSupport + } + ps.Set("mail-client") } - n := strings.IndexByte(address.Address, '@') - if n == -1 { - goto noEmailSupport - } - if address.Address[n+1:] != v.sso.Config.Namespace { - goto noEmailSupport - } - ps.Set("mail-client") } } @@ -170,7 +181,7 @@ noEmailSupport: pages.RenderPageTemplate(rw, "flow-callback", map[string]any{ "ServiceName": h.conf.ServiceName, - "TargetOrigin": v.targetOrigin, + "TargetOrigin": v.target.Url.String(), "TargetMessage": v3, "AccessToken": accessToken, "RefreshToken": refreshToken, diff --git a/server/flow_test.go b/server/flow_test.go index 2621b6e..ea1b4b4 100644 --- a/server/flow_test.go +++ b/server/flow_test.go @@ -29,6 +29,8 @@ const lavenderDomain = "http://localhost:0" const clientAppDomain = "http://localhost:1" const loginDomain = "http://localhost:2" +var clientAppMeta AllowedClient + var testSigner mjwt.Signer var testOidc = &issuer.WellKnownOIDC{ @@ -70,7 +72,7 @@ var testHttpServer = HttpServer{ }, manager: testManager, flowState: cache.New[string, flowStateData](), - services: map[string]struct{}{ + services: map[string]AllowedClient{ clientAppDomain: {}, }, } @@ -88,6 +90,16 @@ func init() { testSigner = mjwt.NewMJwtSigner("https://example.com", key) testHttpServer.signer = testSigner + + parse, err := url.Parse(clientAppDomain) + if err != nil { + panic(err) + } + + clientAppMeta = AllowedClient{ + Url: utils.JsonUrl{URL: parse}, + Permissions: []string{"test-perm"}, + } } func TestFlowPopup(t *testing.T) { @@ -168,8 +180,8 @@ func TestFlowCallback(t *testing.T) { expiryTime := time.Now().Add(15 * time.Minute) nextState := uuid.NewString() testHttpServer.flowState.Set("example.com:"+nextState, flowStateData{ - sso: testOidc, - targetOrigin: clientAppDomain, + sso: testOidc, + target: clientAppMeta, }, expiryTime) testOa2Exchange = func(oa2conf oauth2.Config, ctx context.Context, code string) (*oauth2.Token, error) { diff --git a/server/server.go b/server/server.go index 7b0474b..3d2ad0a 100644 --- a/server/server.go +++ b/server/server.go @@ -17,12 +17,12 @@ type HttpServer struct { manager *issuer.Manager signer mjwt.Signer flowState *cache.Cache[string, flowStateData] - services map[string]struct{} + services map[string]AllowedClient } type flowStateData struct { - sso *issuer.WellKnownOIDC - targetOrigin string + sso *issuer.WellKnownOIDC + target AllowedClient } func NewHttpServer(conf Conf, signer mjwt.Signer) *http.Server { @@ -41,9 +41,9 @@ func NewHttpServer(conf Conf, signer mjwt.Signer) *http.Server { log.Fatal("[Lavender] Failed to create SSO service manager: ", err) } - services := make(map[string]struct{}) + services := make(map[string]AllowedClient) for _, i := range conf.AllowedClients { - services[i.String()] = struct{}{} + services[i.Url.String()] = i } hs := &HttpServer{