mirror of
https://github.com/paralus/paralus.git
synced 2026-03-04 18:10:21 +00:00
SAML Authentication and IDP Service
Initial SAML based authentication in usermgmt component Signed-off-by: Akshay Gaikwad <akshay.gaikwad@rafay.co> Update SAMLAuth middleware - Get username from request body - Validate for exiwstng session - Redirect to IDP authentication when no valid session Modification in SAML middlewares - Reduce duplicate code by new function createSAMLMiddleware Restructure saml package - Moved SAML Middlewares to middleware.go - Embed samlsp.Middleware into own struct Use EntityDAO from common and mock Idp model for testing Signed-off-by: Akshay Gaikwad <akshay.gaikwad@rafay.co> Add IDP API definitions Signed-off-by: Akshay Gaikwad <akshay.gaikwad@rafay.co> Add interface for IdpService Add id to UpdateIdp proto message Add metadata_url to UpdateIdp message and limit to ListIdps rpc Implement Idp Service methods Update Idp model Remove main.go and mocked idp model Generate ACS URL and SAML SP cert Change Id type in proto as well as in IDP model Update IDP model struct tags Set TimeFormat for IDP service Update generateSpCert() and generateAcsURL() Add idpServer which is wrapper around idpService idpServer is a gRPC controller. Add back-end validations for idp service
This commit is contained in:
1
components/usermgmt/env.example
Normal file
1
components/usermgmt/env.example
Normal file
@@ -0,0 +1 @@
|
||||
APP_HOST_HTTP="http://localhost:8000"
|
||||
@@ -5,6 +5,7 @@ go 1.17
|
||||
require (
|
||||
github.com/RafaySystems/rcloud-base/components/adminsrv v0.0.0-unpublished
|
||||
github.com/RafaySystems/rcloud-base/components/common v0.0.0-unpublished
|
||||
github.com/crewjam/saml v0.4.6
|
||||
github.com/gogo/protobuf v1.3.2
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.2
|
||||
@@ -21,20 +22,27 @@ require (
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/beevik/etree v1.1.0 // indirect
|
||||
github.com/crewjam/httperr v0.2.0 // indirect
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/jonboulle/clockwork v0.2.2 // indirect
|
||||
github.com/julienschmidt/httprouter v1.3.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
||||
github.com/magiconair/properties v1.8.5 // indirect
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mitchellh/mapstructure v1.4.3 // indirect
|
||||
github.com/pelletier/go-toml v1.9.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/processout/grpc-go-pool v1.2.1 // indirect
|
||||
github.com/russellhaering/goxmldsig v1.1.1 // indirect
|
||||
github.com/segmentio/asm v1.1.0 // indirect
|
||||
github.com/segmentio/encoding v0.3.2 // indirect
|
||||
github.com/speps/go-hashids v2.0.0+incompatible // indirect
|
||||
|
||||
@@ -76,6 +76,8 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/beevik/etree v1.1.0 h1:T0xke/WvNtMoCqgzPhkX2r4rjY3GDZFi+FjpRZY2Jbs=
|
||||
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
|
||||
github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
@@ -122,9 +124,14 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfc
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
|
||||
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
|
||||
github.com/crewjam/saml v0.4.6 h1:XCUFPkQSJLvzyl4cW9OvpWUbRf0gE7VUpU8ZnilbeM4=
|
||||
github.com/crewjam/saml v0.4.6/go.mod h1:ZBOXnNPFzB3CgOkRm7Nd6IVdkG+l/wF+0ZXLqD96t1A=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dchest/uniuri v0.0.0-20200228104902-7aecb25e1fe5/go.mod h1:GgB8SF9nRG+GqaDtLcwJZsQFhcogVCJ79j4EdT0c2V4=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
@@ -186,6 +193,8 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0 h1:XUgk2Ex5veyVFVeLm0xhusUTQybEbexJXrvPNOKkSY0=
|
||||
github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
|
||||
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
|
||||
@@ -328,6 +337,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
|
||||
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
@@ -354,6 +364,9 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
@@ -365,6 +378,8 @@ github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPK
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU=
|
||||
github.com/mattermost/xml-roundtrip-validator v0.1.0/go.mod h1:qccnGMcpgwcNaBnxqpJpWWUiPNr5H3O8eDgGV9gT5To=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -407,7 +422,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
@@ -435,6 +449,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
|
||||
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -474,6 +489,11 @@ github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40T
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
|
||||
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
|
||||
github.com/russellhaering/goxmldsig v1.1.1 h1:vI0r2osGF1A9PLvsGdPUAGwEIrKa4Pj5sesSBsebIxM=
|
||||
github.com/russellhaering/goxmldsig v1.1.1/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM=
|
||||
@@ -552,6 +572,7 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/zenazn/goji v1.0.1/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
@@ -609,6 +630,7 @@ golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3 h1:0es+/5331RGQPcXlMfP+WrnIIS6dNnNRe0WB02W0F4M=
|
||||
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
@@ -1057,8 +1079,9 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
@@ -1085,6 +1108,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
|
||||
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
34
components/usermgmt/pkg/internal/models/idp.go
Normal file
34
components/usermgmt/pkg/internal/models/idp.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type Idp struct {
|
||||
bun.BaseModel `bun:"table:authsrv_idp,alias:idp"`
|
||||
|
||||
Id uuid.UUID `bun:"id,type:uuid,pk,default:uuid_generate_v4()"`
|
||||
Name string `bun:"name,notnull,unique"`
|
||||
Description string `bun:"description"`
|
||||
CreatedAt time.Time `bun:"created_at,notnull,default:current_timestamp"`
|
||||
ModifiedAt time.Time `bun:"modified_at,notnull,default:current_timestamp"`
|
||||
|
||||
IdpName string `bun:"idp_name,notnull"`
|
||||
Domain string `bun:"domain,notnull,unique"`
|
||||
AcsURL string `bun:"acs_url,notnull,unique"`
|
||||
OrganizationId string `bun:"organization_id,type:uuid"`
|
||||
PartnerId string `bun:"partner_id,type:uuid"`
|
||||
SsoURL string `bun:"sso_url"`
|
||||
IdpCert string `bun:"idp_cert"`
|
||||
SpCert string `bun:"sp_cert"`
|
||||
SpKey string `bun:"sp_key"`
|
||||
MetadataURL string `bun:"metadata_url"`
|
||||
MetadataFilename string `bun:"metadata_filename"`
|
||||
Metadata []byte `bun:"metadata"`
|
||||
GroupAttributeName string `bun:"group_attribute_name"`
|
||||
SaeEnabled bool `bun:"is_sae_enabled"`
|
||||
Trash bool `bun:"trash,default:false"`
|
||||
}
|
||||
33
components/usermgmt/pkg/server/idp.go
Normal file
33
components/usermgmt/pkg/server/idp.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/RafaySystems/rcloud-base/components/usermgmt/pkg/service"
|
||||
rpcv3 "github.com/RafaySystems/rcloud-base/components/usermgmt/proto/rpc/v3"
|
||||
userv3 "github.com/RafaySystems/rcloud-base/components/usermgmt/proto/types/userpb/v3"
|
||||
)
|
||||
|
||||
type idpServer struct {
|
||||
service.IdpService
|
||||
}
|
||||
|
||||
func NewIdpServer(is service.IdpService) rpcv3.IdpServer {
|
||||
return &idpServer{is}
|
||||
}
|
||||
|
||||
func (s *idpServer) CreateIdp(ctx context.Context, idp *userv3.NewIdp) (*userv3.Idp, error) {
|
||||
return s.IdpService.CreateIdp(ctx, idp)
|
||||
}
|
||||
|
||||
func (s *idpServer) UpdateIdp(ctx context.Context, idp *userv3.UpdateIdp) (*userv3.Idp, error) {
|
||||
return s.IdpService.UpdateIdp(ctx, idp)
|
||||
}
|
||||
|
||||
func (s *idpServer) GetSpConfigById(ctx context.Context, idpID *userv3.IdpID) (*userv3.SpConfig, error) {
|
||||
return s.IdpService.GetSpConfigById(ctx, idpID)
|
||||
}
|
||||
|
||||
func (s *idpServer) ListIdps(ctx context.Context, req *userv3.ListIdpsRequest) (*userv3.ListIdpsResponse, error) {
|
||||
return s.IdpService.ListIdps(ctx, req)
|
||||
}
|
||||
280
components/usermgmt/pkg/service/idp.go
Normal file
280
components/usermgmt/pkg/service/idp.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/RafaySystems/rcloud-base/components/common/pkg/persistence/provider/pg"
|
||||
"github.com/RafaySystems/rcloud-base/components/usermgmt/pkg/internal/models"
|
||||
userv3 "github.com/RafaySystems/rcloud-base/components/usermgmt/proto/types/userpb/v3"
|
||||
"github.com/google/uuid"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
const TimeLayout = "2006-01-02T15:04:05.999999Z"
|
||||
|
||||
type IdpService interface {
|
||||
CreateIdp(context.Context, *userv3.NewIdp) (*userv3.Idp, error)
|
||||
UpdateIdp(context.Context, *userv3.UpdateIdp) (*userv3.Idp, error)
|
||||
GetSpConfigById(context.Context, *userv3.IdpID) (*userv3.SpConfig, error)
|
||||
ListIdps(context.Context, *userv3.ListIdpsRequest) (*userv3.ListIdpsResponse, error)
|
||||
}
|
||||
|
||||
type idpService struct {
|
||||
dao pg.EntityDAO
|
||||
}
|
||||
|
||||
func NewIdpService(db *bun.DB) IdpService {
|
||||
return &idpService{
|
||||
dao: pg.NewEntityDAO(db),
|
||||
}
|
||||
}
|
||||
|
||||
func generateAcsURL(baseURL string) string {
|
||||
uuid := uuid.New()
|
||||
acsURL := fmt.Sprintf("%s/%s/", baseURL, uuid.String())
|
||||
return acsURL
|
||||
}
|
||||
|
||||
// generateSpCert generates self signed certificate. Returns cert and
|
||||
// private key.
|
||||
func generateSpCert(host string) (string, string, error) {
|
||||
// generate private key of type rsa
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
privPEM := new(bytes.Buffer)
|
||||
err = pem.Encode(privPEM, &pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
privPEMBytes, err := ioutil.ReadAll(privPEM)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
template := &x509.Certificate{
|
||||
SerialNumber: big.NewInt(1000),
|
||||
Subject: pkix.Name{
|
||||
Organization: []string{"Rafay"},
|
||||
Country: []string{"US"},
|
||||
},
|
||||
NotBefore: time.Now(),
|
||||
NotAfter: time.Now().AddDate(30, 0, 0),
|
||||
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
|
||||
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
|
||||
DNSNames: []string{host},
|
||||
}
|
||||
// generate self sign certificate
|
||||
cBytes, err := x509.CreateCertificate(rand.Reader, template, template, &priv.PublicKey, priv)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
cPEM := new(bytes.Buffer)
|
||||
err = pem.Encode(cPEM, &pem.Block{
|
||||
Type: "CERTIFICATE",
|
||||
Bytes: cBytes,
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
cPEMBytes, err := ioutil.ReadAll(cPEM)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(cPEMBytes), string(privPEMBytes), nil
|
||||
}
|
||||
|
||||
func (s *idpService) CreateIdp(ctx context.Context, idp *userv3.NewIdp) (*userv3.Idp, error) {
|
||||
name := idp.GetName()
|
||||
domain := idp.GetDomain()
|
||||
|
||||
e := &models.Idp{}
|
||||
s.dao.GetByName(ctx, name, e)
|
||||
if e.Name == name {
|
||||
return &userv3.Idp{}, fmt.Errorf("DUPLICATE NAME")
|
||||
}
|
||||
s.dao.GetX(ctx, "domain", domain, e)
|
||||
if e.Domain == domain {
|
||||
return &userv3.Idp{}, fmt.Errorf("DUPLICATE DOMAIN")
|
||||
}
|
||||
|
||||
base, err := url.Parse(os.Getenv("APP_HOST_HTTP"))
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
acsURL := generateAcsURL(base.String())
|
||||
entity := &models.Idp{
|
||||
Name: name,
|
||||
IdpName: idp.GetIdpName(),
|
||||
Domain: domain,
|
||||
AcsURL: acsURL,
|
||||
GroupAttributeName: idp.GetGroupAttributeName(),
|
||||
SaeEnabled: idp.GetIsSaeEnabled(),
|
||||
}
|
||||
if entity.SaeEnabled {
|
||||
spcert, spkey, err := generateSpCert(base.Host)
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
entity.SpCert = spcert
|
||||
entity.SpKey = spkey
|
||||
}
|
||||
_, err = s.dao.Create(ctx, entity)
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
|
||||
rv := &userv3.Idp{
|
||||
Id: entity.Id.String(),
|
||||
Name: entity.Name,
|
||||
IdpName: entity.IdpName,
|
||||
Domain: entity.Domain,
|
||||
AcsUrl: entity.AcsURL,
|
||||
SsoUrl: entity.SsoURL,
|
||||
IdpCert: entity.IdpCert,
|
||||
SpCert: entity.SpCert,
|
||||
MetadataUrl: entity.MetadataURL,
|
||||
MetadataFilename: entity.MetadataFilename,
|
||||
IsSaeEnabled: entity.SaeEnabled,
|
||||
GroupAttributeName: entity.GroupAttributeName,
|
||||
OrganizationId: entity.OrganizationId,
|
||||
PartnerId: entity.PartnerId,
|
||||
CreatedAt: entity.CreatedAt.Format(TimeLayout),
|
||||
ModifiedAt: entity.ModifiedAt.Format(TimeLayout),
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *idpService) UpdateIdp(ctx context.Context, new *userv3.UpdateIdp) (*userv3.Idp, error) {
|
||||
id, err := uuid.Parse(new.GetId())
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
entity := &models.Idp{
|
||||
Id: id,
|
||||
Name: new.GetName(),
|
||||
ModifiedAt: time.Now(),
|
||||
IdpName: new.GetIdpName(),
|
||||
Domain: new.GetDomain(),
|
||||
AcsURL: new.GetAcsUrl(),
|
||||
MetadataURL: new.GetMetadataUrl(),
|
||||
GroupAttributeName: new.GetGroupAttributeName(),
|
||||
SaeEnabled: new.GetIsSaeEnabled(),
|
||||
}
|
||||
if entity.SaeEnabled {
|
||||
base, err := url.Parse(os.Getenv("APP_HOST_HTTP"))
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
spcert, spkey, err := generateSpCert(base.Host)
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
entity.SpCert = spcert
|
||||
entity.SpKey = spkey
|
||||
}
|
||||
_, err = s.dao.Update(ctx, id, entity)
|
||||
if err != nil {
|
||||
return &userv3.Idp{}, err
|
||||
}
|
||||
rv := &userv3.Idp{
|
||||
Id: entity.Id.String(),
|
||||
Name: entity.Name,
|
||||
IdpName: entity.IdpName,
|
||||
Domain: entity.Domain,
|
||||
AcsUrl: entity.AcsURL,
|
||||
SsoUrl: entity.SsoURL,
|
||||
IdpCert: entity.IdpCert,
|
||||
SpCert: entity.SpCert,
|
||||
MetadataUrl: entity.MetadataURL,
|
||||
MetadataFilename: entity.MetadataFilename,
|
||||
IsSaeEnabled: entity.SaeEnabled,
|
||||
GroupAttributeName: entity.GroupAttributeName,
|
||||
OrganizationId: entity.OrganizationId,
|
||||
PartnerId: entity.PartnerId,
|
||||
CreatedAt: entity.CreatedAt.Format(TimeLayout),
|
||||
ModifiedAt: entity.ModifiedAt.Format(TimeLayout),
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *idpService) GetSpConfigById(ctx context.Context, idpID *userv3.IdpID) (*userv3.SpConfig, error) {
|
||||
id, err := uuid.Parse(idpID.GetId())
|
||||
if err != nil {
|
||||
return &userv3.SpConfig{}, err
|
||||
}
|
||||
|
||||
entity := &models.Idp{}
|
||||
_, err = s.dao.GetByID(ctx, id, entity)
|
||||
if err != nil {
|
||||
return &userv3.SpConfig{}, err
|
||||
}
|
||||
if entity.Id != id {
|
||||
return &userv3.SpConfig{}, fmt.Errorf("IDP ID DOES NOT EXISTS")
|
||||
}
|
||||
rv := &userv3.SpConfig{
|
||||
NameidFormat: "Email Address",
|
||||
ConsumerBinding: "HTTP-POST",
|
||||
AcsUrl: entity.AcsURL,
|
||||
EntityId: entity.AcsURL,
|
||||
GroupAttributeName: entity.GroupAttributeName,
|
||||
SpCert: entity.SpCert,
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
func (s *idpService) ListIdps(ctx context.Context, req *userv3.ListIdpsRequest) (*userv3.ListIdpsResponse, error) {
|
||||
entities := []*models.Idp{}
|
||||
var orgID uuid.NullUUID
|
||||
var parID uuid.NullUUID
|
||||
s.dao.List(ctx, parID, orgID, entities)
|
||||
|
||||
// Get idps only till limit
|
||||
var result []*userv3.Idp
|
||||
for _, entity := range entities {
|
||||
e := &userv3.Idp{
|
||||
Id: entity.Id.String(),
|
||||
Name: entity.Name,
|
||||
IdpName: entity.IdpName,
|
||||
Domain: entity.Domain,
|
||||
AcsUrl: entity.AcsURL,
|
||||
SsoUrl: entity.SsoURL,
|
||||
IdpCert: entity.IdpCert,
|
||||
SpCert: entity.SpCert,
|
||||
MetadataUrl: entity.MetadataURL,
|
||||
MetadataFilename: entity.MetadataFilename,
|
||||
IsSaeEnabled: entity.SaeEnabled,
|
||||
GroupAttributeName: entity.GroupAttributeName,
|
||||
OrganizationId: entity.OrganizationId,
|
||||
PartnerId: entity.PartnerId,
|
||||
CreatedAt: entity.CreatedAt.Format(TimeLayout),
|
||||
ModifiedAt: entity.ModifiedAt.Format(TimeLayout),
|
||||
}
|
||||
result = append(result, e)
|
||||
}
|
||||
|
||||
rv := &userv3.ListIdpsResponse{
|
||||
Count: int32(len(entities)),
|
||||
Next: 0,
|
||||
Previous: 0,
|
||||
Result: result,
|
||||
}
|
||||
return rv, nil
|
||||
}
|
||||
167
components/usermgmt/pkg/sso/saml/middleware.go
Normal file
167
components/usermgmt/pkg/sso/saml/middleware.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/RafaySystems/rcloud-base/components/usermgmt/pkg/internal/models"
|
||||
"github.com/crewjam/saml"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
)
|
||||
|
||||
func newSAMLMiddlewareFromIDP(idp models.Idp) (*SAMLMiddleware, error) {
|
||||
rootURL, err := url.Parse(os.Getenv("APP_HOST_HTTP"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var idpMetadata *saml.EntityDescriptor
|
||||
if len(idp.Metadata) == 0 {
|
||||
idpMetadataURL, err := url.Parse(idp.MetadataURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
idpMetadata, err = samlsp.FetchMetadata(context.Background(), http.DefaultClient,
|
||||
*idpMetadataURL)
|
||||
} else {
|
||||
idpMetadata, err = samlsp.ParseMetadata(idp.Metadata)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
acsURL, err := url.Parse(idp.AcsURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyPair, err := tls.X509KeyPair([]byte(idp.SpCert), []byte(idp.SpKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyPair.Leaf, err = x509.ParseCertificate(keyPair.Certificate[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := samlsp.Options{
|
||||
EntityID: "",
|
||||
URL: *rootURL,
|
||||
Key: keyPair.PrivateKey.(*rsa.PrivateKey),
|
||||
Certificate: keyPair.Leaf,
|
||||
AllowIDPInitiated: false,
|
||||
DefaultRedirectURI: "/prelogin",
|
||||
IDPMetadata: idpMetadata,
|
||||
SignRequest: false,
|
||||
}
|
||||
sp := samlsp.DefaultServiceProvider(opts)
|
||||
sp.AcsURL = *acsURL
|
||||
m := &samlsp.Middleware{
|
||||
ServiceProvider: sp,
|
||||
Binding: "",
|
||||
ResponseBinding: saml.HTTPPostBinding,
|
||||
OnError: samlsp.DefaultOnError,
|
||||
Session: samlsp.DefaultSessionProvider(opts),
|
||||
}
|
||||
m.RequestTracker = samlsp.DefaultRequestTracker(opts, &m.ServiceProvider)
|
||||
if opts.UseArtifactResponse {
|
||||
m.ResponseBinding = saml.HTTPArtifactBinding
|
||||
}
|
||||
return &SAMLMiddleware{m}, nil
|
||||
}
|
||||
|
||||
// SAMLAuth is an authentication middleware.
|
||||
func (s *SAMLService) SAMLAuth(handler http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "failed to parse form data", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
username := r.PostForm.Get("username")
|
||||
|
||||
if !strings.Contains(username, "@") {
|
||||
http.Error(w, "Invalid email address", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
domain := strings.SplitN(username, "@", 2)[1]
|
||||
|
||||
entity, err := s.EntityDAO.GetX(context.Background(), "domain", domain, &models.Idp{})
|
||||
if err != nil {
|
||||
http.Error(w, "No idp found for domain", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
idp, ok := entity.(models.Idp)
|
||||
if !ok {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := newSAMLMiddlewareFromIDP(idp)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
session, err := m.Session.GetSession(r)
|
||||
if session != nil {
|
||||
r = r.WithContext(samlsp.ContextWithSession(r.Context(), session))
|
||||
w.Write([]byte("authentiated successfully"))
|
||||
return
|
||||
}
|
||||
if err == samlsp.ErrNoSession {
|
||||
m.HandleStartAuthFlow(w, r)
|
||||
return
|
||||
} else {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ServeACS performs SAML Response assertions.
|
||||
func (s *SAMLService) ServeACS(w http.ResponseWriter, r *http.Request) {
|
||||
r.ParseForm()
|
||||
base, _ := url.Parse(os.Getenv("APP_HOST_HTTP"))
|
||||
acsURL := base.ResolveReference(r.URL)
|
||||
|
||||
entity, err := s.EntityDAO.GetX(context.Background(), "acs_url", acsURL.String(), &models.Idp{})
|
||||
if err != nil {
|
||||
http.Error(w, "No Idp for ACS URL", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
idp, ok := entity.(models.Idp)
|
||||
if !ok {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
m, err := newSAMLMiddlewareFromIDP(idp)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
possibleRequestIDs := []string{}
|
||||
if m.ServiceProvider.AllowIDPInitiated {
|
||||
possibleRequestIDs = append(possibleRequestIDs, "")
|
||||
}
|
||||
|
||||
trackedRequests := m.RequestTracker.GetTrackedRequests(r)
|
||||
for _, tr := range trackedRequests {
|
||||
possibleRequestIDs = append(possibleRequestIDs, tr.SAMLRequestID)
|
||||
}
|
||||
assertion, err := m.ServiceProvider.ParseResponse(r, possibleRequestIDs)
|
||||
if err != nil {
|
||||
m.OnError(w, r, err)
|
||||
return
|
||||
}
|
||||
m.CreateSessionFromAssertion(w, r, assertion, m.ServiceProvider.DefaultRedirectURI)
|
||||
return
|
||||
}
|
||||
21
components/usermgmt/pkg/sso/saml/saml.go
Normal file
21
components/usermgmt/pkg/sso/saml/saml.go
Normal file
@@ -0,0 +1,21 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
pg "github.com/RafaySystems/rcloud-base/components/common/pkg/persistence/provider/pg"
|
||||
"github.com/crewjam/saml/samlsp"
|
||||
"github.com/uptrace/bun"
|
||||
)
|
||||
|
||||
type SAMLMiddleware struct {
|
||||
*samlsp.Middleware
|
||||
}
|
||||
|
||||
type SAMLService struct {
|
||||
EntityDAO pg.EntityDAO
|
||||
}
|
||||
|
||||
func NewSAMLService(db *bun.DB) *SAMLService {
|
||||
return &SAMLService{
|
||||
EntityDAO: pg.NewEntityDAO(db),
|
||||
}
|
||||
}
|
||||
1
components/usermgmt/pkg/sso/saml/saml_test.go
Normal file
1
components/usermgmt/pkg/sso/saml/saml_test.go
Normal file
@@ -0,0 +1 @@
|
||||
package saml
|
||||
22
components/usermgmt/proto/rpc/v3/idp.proto
Normal file
22
components/usermgmt/proto/rpc/v3/idp.proto
Normal file
@@ -0,0 +1,22 @@
|
||||
syntax = "proto3";
|
||||
package rafay.dev.rpc.v3;
|
||||
|
||||
import "proto/types/userpb/v3/idp.proto";
|
||||
|
||||
service Idp {
|
||||
// endpoint POST /auth/v1/sso/idp
|
||||
rpc CreateIdp(rafay.dev.types.user.v3.NewIdp) returns (rafay.dev.types.user.v3.Idp) {};
|
||||
|
||||
// endpoint PUT /auth/v1/sso/idp
|
||||
rpc UpdateIdp(rafay.dev.types.user.v3.UpdateIdp) returns (rafay.dev.types.user.v3.Idp) {};
|
||||
|
||||
// endpoint /auth/v1/sso/idp/dk351mn/spconfig/
|
||||
rpc GetSpConfigById(rafay.dev.types.user.v3.IdpID) returns (rafay.dev.types.user.v3.SpConfig) {};
|
||||
|
||||
// endpoint /auth/v1/sso/idp/?limit=1000
|
||||
rpc ListIdps(rafay.dev.types.user.v3.ListIdpsRequest) returns (rafay.dev.types.user.v3.ListIdpsResponse) {};
|
||||
|
||||
// endpooint /auth/v1/sso/idp/5m16w2y/upload_metadata/
|
||||
// file content as request payload and response is Idp
|
||||
}
|
||||
|
||||
64
components/usermgmt/proto/types/userpb/v3/idp.proto
Normal file
64
components/usermgmt/proto/types/userpb/v3/idp.proto
Normal file
@@ -0,0 +1,64 @@
|
||||
syntax = "proto3";
|
||||
package rafay.dev.types.user.v3;
|
||||
|
||||
message Idp {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string idp_name = 3;
|
||||
string domain = 4;
|
||||
string acs_url = 5;
|
||||
string sso_url = 6;
|
||||
string idp_cert = 7;
|
||||
string sp_cert = 8;
|
||||
string metadata_url = 9;
|
||||
string metadata_filename = 10;
|
||||
bool is_sae_enabled = 11;
|
||||
string group_attribute_name = 12;
|
||||
string organization_id = 13;
|
||||
string partner_id = 14;
|
||||
string created_at = 15;
|
||||
string modified_at = 16;
|
||||
}
|
||||
|
||||
message NewIdp {
|
||||
string domain = 1;
|
||||
string group_attribute_name = 2;
|
||||
string idp_name = 3;
|
||||
bool is_sae_enabled = 4;
|
||||
string name = 5;
|
||||
}
|
||||
|
||||
message UpdateIdp {
|
||||
string id = 1;
|
||||
string acs_url = 2;
|
||||
string domain = 3;
|
||||
string group_attribute_name = 4;
|
||||
string idp_name = 5;
|
||||
bool is_sae_enabled = 6;
|
||||
string metadata_url = 7;
|
||||
string name = 8;
|
||||
}
|
||||
|
||||
message IdpID {
|
||||
string id = 1;
|
||||
}
|
||||
|
||||
message SpConfig {
|
||||
string nameid_format = 1;
|
||||
string consumer_binding = 2;
|
||||
string acs_url = 3;
|
||||
string entity_id = 4;
|
||||
string group_attribute_name = 5;
|
||||
string sp_cert = 6;
|
||||
}
|
||||
|
||||
message ListIdpsResponse {
|
||||
int32 count = 1;
|
||||
int32 next = 2;
|
||||
int32 previous = 3;
|
||||
repeated Idp result = 4;
|
||||
}
|
||||
|
||||
message ListIdpsRequest {
|
||||
int32 limit = 1;
|
||||
}
|
||||
Reference in New Issue
Block a user