Skip to content

Commit

Permalink
Merge pull request #1865 from ansible-semaphore/email_claim_format
Browse files Browse the repository at this point in the history
feat: add format for oidc claims
  • Loading branch information
fiftin committed Mar 24, 2024
2 parents 52f64e3 + 54587b0 commit f982f3d
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 24 deletions.
64 changes: 41 additions & 23 deletions api/login.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"bytes"
"context"
"crypto/tls"
"encoding/base64"
Expand All @@ -12,8 +13,8 @@ import (
"net/url"
"os"
"sort"
"strconv"
"strings"
"text/template"
"time"

"golang.org/x/crypto/bcrypt"
Expand Down Expand Up @@ -425,38 +426,55 @@ type oidcClaimResult struct {
email string
}

func parseClaims(claims map[string]interface{}, provider util.OidcProvider) (res oidcClaimResult, err error) {
var ok bool
func parseClaim(str string, claims map[string]interface{}) (string, bool) {

res.email, ok = claims[provider.EmailClaim].(string)
if !ok {
for _, s := range strings.Split(str, "|") {
s = strings.TrimSpace(s)

var username string
if strings.Contains(s, "{{") {
tpl, err := template.New("").Parse(s)

if provider.EmailSuffix == "" {
err = fmt.Errorf("claim '%s' missing from id_token or not a string", provider.EmailClaim)
return
if err != nil {
return "", false
}

email := bytes.NewBufferString("")

if err = tpl.Execute(email, claims); err != nil {
return "", false
}

res := email.String()

return res, res != ""
}

switch claims[provider.UsernameClaim].(type) {
case float64:
username = strconv.FormatFloat(claims[provider.UsernameClaim].(float64), 'f', -1, 64)
case string:
username = claims[provider.UsernameClaim].(string)
default:
err = fmt.Errorf("claim '%s' missing from id_token or not a string or an number", provider.UsernameClaim)
b, _ := json.MarshalIndent(claims, "", " ")
fmt.Print(string(b))
return
res, ok := claims[s].(string)
if res != "" && ok {
return res, ok
}
}

res.email = username + "@" + provider.EmailSuffix
return "", false
}

func parseClaims(claims map[string]interface{}, provider util.OidcProvider) (res oidcClaimResult, err error) {

var ok bool
res.email, ok = parseClaim(provider.EmailClaim, claims)

if !ok {
err = fmt.Errorf("claim '%s' missing or has bad format", provider.EmailClaim)
return
}

res.username = getRandomUsername()
res.username, ok = parseClaim(provider.UsernameClaim, claims)
if !ok {
res.username = getRandomUsername()
}

res.name, ok = claims[provider.NameClaim].(string)
if !ok || res.name == "" {
res.name, ok = parseClaim(provider.NameClaim, claims)
if !ok {
res.name = getRandomProfileName()
}

Expand Down
55 changes: 55 additions & 0 deletions api/login_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package api

import (
"testing"
)

func TestParseClaim(t *testing.T) {
claims := map[string]interface{}{
"username": "fiftin",
"email": "",
"id": 1234567,
}

res, ok := parseClaim("email | {{ .id }}@test.com", claims)

if !ok {
t.Fail()
}

if res != "1234567@test.com" {
t.Fatalf("%s must be %d@test.com", res, claims["id"])
}
}

func TestParseClaim2(t *testing.T) {
claims := map[string]interface{}{
"username": "fiftin",
"email": "",
"id": 1234567,
}

res, ok := parseClaim("username", claims)

if !ok {
t.Fail()
}

if res != claims["username"] {
t.Fail()
}
}

func TestParseClaim3(t *testing.T) {
claims := map[string]interface{}{
"username": "fiftin",
"email": "",
"id": 1234567,
}

_, ok := parseClaim("email", claims)

if ok {
t.Fail()
}
}
1 change: 0 additions & 1 deletion util/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ type OidcProvider struct {
UsernameClaim string `json:"username_claim" default:"preferred_username"`
NameClaim string `json:"name_claim" default:"preferred_username"`
EmailClaim string `json:"email_claim" default:"email"`
EmailSuffix string `json:"email_suffix"`
Order int `json:"order"`
}

Expand Down

0 comments on commit f982f3d

Please sign in to comment.