Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions idp/authmiddleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -295,3 +295,11 @@ func AuthSessionIdFromCtx(ctx context.Context) (string, error) {
}
return authSessionId, nil
}

func AppFromCtx(ctx context.Context) (appName string, isApp bool) {
if principal, err := PrincipalFromCtx(ctx); err == nil {
return principal.App()
} else {
return "", false
}
}
59 changes: 59 additions & 0 deletions idp/authmiddleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,52 @@ func TestRequestAsInternalUserAndExternalValidationIsAllowed_PopulatesContextWit
}
}

func TestRequestAsApp_PopulatesContextWithPrincipalAndAuthsession(t *testing.T) {
req, err := http.NewRequest("GET", "/myresource/subresource?query1=abc&query2=123", nil)
if err != nil {
t.Fatal(err)
}
const authSessionId = "2XGxJeb0q+/fS8biFi8FE7TovJPPEPyzlDxT6bh5p5pHA/x7CEi1w9egVhEMz8IWasdfJRFnkSqJnLr61cOKf/i5eWuu7Duh+OTtTjMOt9w=&Bnh4NNU90wH_OVlgbzbdZOEu1aSuPlbUctiCdYTonZ3Ap_Zd3bVL79I-dPdHf4OOgO8NKEdqyLsqc8RhAOreXgJqXuqsreeI"
principal := scim.Principal{Id: "some-app@app.idp.d-velop.local"}
req.Header.Set("Authorization", "Bearer "+authSessionId)
handlerSpy := new(handlerSpy)
idpStub := newIdpStub(map[string]scim.Principal{authSessionId: principal}, nil)
defer idpStub.Close()

idp.HandleAuth(returnFromCtx(idpStub.URL), returnFromCtx("1"), true, log, log)(handlerSpy).ServeHTTP(httptest.NewRecorder(), req)

if err := handlerSpy.assertAuthSessionIdIs(authSessionId); err != nil {
t.Error(err)
}

if err := handlerSpy.assertPrincipalIsApp(true, "some-app"); err != nil {
t.Error(err)
}
}

func TestRequestAsNonApp_RequestAppPrincipal_ReturnsNoApp(t *testing.T) {
req, err := http.NewRequest("GET", "/myresource/subresource?query1=abc&query2=123", nil)
if err != nil {
t.Fatal(err)
}
const authSessionId = "2XGxJeb0q+/fS8biFi8FE7TovJPPEPyzlDxT6bh5p6pHA/x7CEi1w9egVhEMz8IWasdfJRFnkSqJnLr61cOKf/i5eWuu7Duh+OTtTjMOt9w=&Bnh4NNU90wH_OVlgbzbdZOEu1aSuPlbUctiCdYTonZ3Ap_Zd3bVL79I-dPdHf4OOgO8NKEdqyLsqc8RhAOreXgJqXuqsreeI"
principal := scim.Principal{Id: "7bbcf1b6-017a-449a-ad5f-9723d28223e1"}
req.Header.Set("Authorization", "Bearer "+authSessionId)
handlerSpy := new(handlerSpy)
idpStub := newIdpStub(map[string]scim.Principal{authSessionId: principal}, nil)
defer idpStub.Close()

idp.HandleAuth(returnFromCtx(idpStub.URL), returnFromCtx("1"), true, log, log)(handlerSpy).ServeHTTP(httptest.NewRecorder(), req)

if err := handlerSpy.assertAuthSessionIdIs(authSessionId); err != nil {
t.Error(err)
}

if err := handlerSpy.assertPrincipalIsApp(false, ""); err != nil {
t.Error(err)
}
}

func TestIdpSendsNoCacheHeader_CallsIdp(t *testing.T) {
req, err := http.NewRequest("GET", "/myresource/subresource?query1=abc&query2=123", nil)
if err != nil {
Expand Down Expand Up @@ -752,12 +798,15 @@ type handlerSpy struct {
authSessionId string
prinicpal scim.Principal
hasBeenCalled bool
app string
isApp bool
}

func (spy *handlerSpy) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
spy.hasBeenCalled = true
spy.authSessionId, _ = idp.AuthSessionIdFromCtx(r.Context())
spy.prinicpal, _ = idp.PrincipalFromCtx(r.Context())
spy.app, spy.isApp = idp.AppFromCtx(r.Context())
}

func (spy *handlerSpy) assertAuthSessionIdIs(expectedAuthSessionID string) error {
Expand All @@ -774,6 +823,16 @@ func (spy *handlerSpy) assertPrincipalIs(expectedPrincipal scim.Principal) error
return nil
}

func (spy *handlerSpy) assertPrincipalIsApp(expectIsApp bool, expectedApp string) error {
if spy.isApp != expectIsApp {
return fmt.Errorf("handler set isApp = '%v', want '%v'", spy.isApp, expectIsApp)
}
if spy.app != expectedApp {
return fmt.Errorf("handler set wrong app, got %v, want %v", spy.app, expectedApp)
}
return nil
}

type responseSpy struct {
*httptest.ResponseRecorder
}
Expand Down
10 changes: 10 additions & 0 deletions idp/scim/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ package scim

import (
"encoding/json"
"strings"
)

const appSessionIdSuffix = "@app.idp.d-velop.local"

// Principal represents a user.
//
// It complies to the SCIM User Schema.
Expand Down Expand Up @@ -70,6 +73,13 @@ func (p Principal) String() string {
return string(b)
}

func (p Principal) App() (appName string, isApp bool) {
if strings.HasSuffix(p.Id, appSessionIdSuffix) {
return strings.TrimSuffix(p.Id, appSessionIdSuffix), true
}
return "", false
}

type UserName struct {
// Formatted is the full name, including all middle names, titles, and suffixes as appropriate, formatted for display (e.g. Ms. Barbara Jane Jensen, III.).
Formatted string `json:"formatted"`
Expand Down
25 changes: 25 additions & 0 deletions idp/scim/user_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,28 @@ func TestCanDeserializeSCIMUser(t *testing.T) {
t.Errorf("Unmarshaled Object wrong: got \n %v want\n %v", u, donaldDuck)
}
}

func TestUserHasAppId_ReturnsApp( t *testing.T) {
principal := scim.Principal{Id: "some-app@app.idp.d-velop.local"}
app, isApp := principal.App()

if !isApp {
t.Errorf("expected isApp = true, got false")
}

if app != "some-app" {
t.Errorf("expected app = 'some-app', got %v", app)
}
}

func TestUserHasNonAppId_ReturnsNoApp( t *testing.T) {
appName, isApp := donaldDuck.App()

if isApp {
t.Errorf("expected isApp = false")
}

if appName != "" {
t.Errorf("expected appName = '', got appNAme = '%v'", appName)
}
}