From 96ce51e51e97478df5140ada59d00fb2cb27f5b0 Mon Sep 17 00:00:00 2001 From: Gabriel Zachmann Date: Fri, 21 Oct 2022 12:32:00 +0200 Subject: [PATCH 01/55] Update go.yml --- .github/workflows/go.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index c767e0d3..0c4158c0 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -2,9 +2,9 @@ name: Go on: push: - branches: [ master ] + branches: [ master, prerelease ] pull_request: - branches: [ master ] + branches: [ master, prerelease ] jobs: @@ -13,10 +13,10 @@ jobs: runs-on: ubuntu-latest steps: - - name: Set up Go 1.16 + - name: Set up Go 1.19 uses: actions/setup-go@v2 with: - go-version: 1.16 + go-version: 1.19 id: go - name: Check out code into the Go module directory From d63ecb33ddc1dca63d0433b8541da87c3a479039 Mon Sep 17 00:00:00 2001 From: zachmann Date: Wed, 19 Oct 2022 10:35:36 +0200 Subject: [PATCH 02/55] fix webinterface at scope selection indicator --- internal/server/web/partials/scripts.mustache | 4 +++ internal/server/web/static/js/create-at.js | 3 ++- internal/server/web/static/js/restr-gui.js | 24 +---------------- internal/server/web/static/js/scope.js | 26 +++++++++++++++++++ 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 internal/server/web/static/js/scope.js diff --git a/internal/server/web/partials/scripts.mustache b/internal/server/web/partials/scripts.mustache index 0de4eab7..9a086c39 100644 --- a/internal/server/web/partials/scripts.mustache +++ b/internal/server/web/partials/scripts.mustache @@ -17,6 +17,7 @@ + @@ -64,5 +65,8 @@ crossorigin="anonymous" referrerpolicy="no-referrer" > + {{^home}} + + {{/home}} {{/restr-gui}} diff --git a/internal/server/web/static/js/create-at.js b/internal/server/web/static/js/create-at.js index 1ceed26c..8ea2466a 100644 --- a/internal/server/web/static/js/create-at.js +++ b/internal/server/web/static/js/create-at.js @@ -131,7 +131,8 @@ function initAT(...next) { } let $table = $('#at-scopeTableBody'); for (const scope of scopes) { - _addScopeValueToGUI(scope, $table, "at"); + _addScopeValueToGUI(scope, $table, "", "at"); } + initScopesGUI("at"); doNext(...next); } \ No newline at end of file diff --git a/internal/server/web/static/js/restr-gui.js b/internal/server/web/static/js/restr-gui.js index ed673a39..b17be780 100644 --- a/internal/server/web/static/js/restr-gui.js +++ b/internal/server/web/static/js/restr-gui.js @@ -94,29 +94,7 @@ function initRestrGUI(prefix = "") { } } - $('.scope-checkbox[instance-prefix="' + prefix + '"]').on("click", function () { - let allScopesInactive = $(this).parents('tbody').find('.scope-inactive'); - let allScopesActive = $(this).parents('tbody').find('.scope-active'); - let checkedScopeBoxes = $(this).parents('tbody').find('.scope-checkbox:checked'); - let activeIcon = $(this).parents('tr').find('.scope-active'); - let inactiveIcon = $(this).parents('tr').find('.scope-inactive'); - let activated = $(this).prop('checked'); - if (activated) { - if (checkedScopeBoxes.length === 1) { // There was no box checked before, but now one has been checked - allScopesActive.hideB(); - allScopesInactive.showB(); - } - inactiveIcon.hideB(); - activeIcon.showB(); - } else { - activeIcon.hideB(); - inactiveIcon.showB(); - } - if (checkedScopeBoxes.length === 0) { - allScopesInactive.hideB(); - allScopesActive.showB(); - } - }) + initScopesGUI(prefix); scopeTableBody(prefix).find('.scope-checkbox').on("click", function () { GUIToRestr_Scopes(prefix); }) diff --git a/internal/server/web/static/js/scope.js b/internal/server/web/static/js/scope.js new file mode 100644 index 00000000..44d7eb41 --- /dev/null +++ b/internal/server/web/static/js/scope.js @@ -0,0 +1,26 @@ +function initScopesGUI(prefix = "") { + $('.scope-checkbox[instance-prefix="' + prefix + '"]').on("click", function () { + let allScopesInactive = $(this).parents('tbody').find('.scope-inactive'); + let allScopesActive = $(this).parents('tbody').find('.scope-active'); + let checkedScopeBoxes = $(this).parents('tbody').find('.scope-checkbox:checked'); + let activeIcon = $(this).parents('tr').find('.scope-active'); + let inactiveIcon = $(this).parents('tr').find('.scope-inactive'); + let activated = $(this).prop('checked'); + if (activated) { + if (checkedScopeBoxes.length === 1) { // There was no box checked before, but now one has been checked + allScopesActive.hideB(); + allScopesInactive.showB(); + } + inactiveIcon.hideB(); + activeIcon.showB(); + } else { + activeIcon.hideB(); + inactiveIcon.showB(); + } + if (checkedScopeBoxes.length === 0) { + allScopesInactive.hideB(); + allScopesActive.showB(); + } + }) + +} From 12b21031ea95b27b80941dd5219b1467a7a58286 Mon Sep 17 00:00:00 2001 From: zachmann Date: Wed, 19 Oct 2022 10:39:54 +0200 Subject: [PATCH 03/55] bump version --- CHANGELOG.md | 6 ++++++ internal/model/version/version.go | 2 +- .../server/web/static/img/mytoken-grey.png | Bin 8981 -> 8965 bytes internal/server/web/static/img/mytoken.png | Bin 9008 -> 8987 bytes mytoken.png | Bin 9008 -> 8987 bytes 5 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eaaf274c..ed0651ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,12 @@ +## mytoken 0.6.1 + +### Bugfixes + +- Fixed a bug in the web interface where the scope selection indicator for access tokens where not updated. + ## mytoken 0.6.0 ### API diff --git a/internal/model/version/version.go b/internal/model/version/version.go index 41b944b6..0e5d19f0 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -8,7 +8,7 @@ import ( const ( MAJOR = 0 MINOR = 6 - FIX = 0 + FIX = 1 DEV = false ) diff --git a/internal/server/web/static/img/mytoken-grey.png b/internal/server/web/static/img/mytoken-grey.png index 4c86eb0acdd40a9bef20733e7db3e31c7e055de7..3e4e9e0ec31568cb18aa1c60eb00ee1e5cdd32d1 100644 GIT binary patch literal 8965 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 J@}WV@ diff --git a/internal/server/web/static/img/mytoken.png b/internal/server/web/static/img/mytoken.png index 9f3b572b875c7e52aad179c2754042f14f58c146..4e4d9be672e8e084443ea389552d3a4e8c66219b 100644 GIT binary patch literal 8987 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 diff --git a/mytoken.png b/mytoken.png index 9f3b572b875c7e52aad179c2754042f14f58c146..4e4d9be672e8e084443ea389552d3a4e8c66219b 100644 GIT binary patch literal 8987 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 From 24f423b9b99e44c1930fba7c791d34474308733a Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 20 Oct 2022 15:04:04 +0200 Subject: [PATCH 04/55] allow host names in restrictions, not only ips --- CHANGELOG.md | 14 + config/example-config.yaml | 5 +- docker/example-docker-config.yaml | 15 +- go.mod | 5 +- go.sum | 11 +- internal/endpoints/consent/pkg/restriction.go | 2 +- internal/model/restrictionKey.go | 4 +- internal/server/middlerwares.go | 4 +- .../partials/restrictions-gui-editor.mustache | 20 +- .../server/web/partials/restrictions.mustache | 2 +- internal/server/web/static/js/restr-gui.js | 24 +- internal/server/web/static/js/restrictions.js | 25 +- internal/utils/cache/cache.go | 62 +++ internal/utils/iputils/iputils.go | 165 +++++++ shared/mytoken/restrictions/restriction.go | 64 ++- .../mytoken/restrictions/restriction_test.go | 416 ++++++++++++++++-- shared/utils/utils.go | 49 --- 17 files changed, 726 insertions(+), 161 deletions(-) create mode 100644 internal/utils/cache/cache.go create mode 100644 internal/utils/iputils/iputils.go diff --git a/CHANGELOG.md b/CHANGELOG.md index ed0651ad..2fe9079a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,20 @@ ## mytoken 0.6.1 +### API + +- Changed the restriction `ip` key to `hosts`: + - Backward compatibility is preserved. The legacy key `ip` is still accepted. + - The `hosts` entry can contain: + - Single ip address + - Subnet address + - Host name (with or without wildcard) + - To compare against this, on request a reverse dns lookup is done for the request's ip address + +### Enhancements + +- Location restriction can now be done with host names, not only plain ip addresses, see above for more details. + ### Bugfixes - Fixed a bug in the web interface where the scope selection indicator for access tokens where not updated. diff --git a/config/example-config.yaml b/config/example-config.yaml index 5957ec6a..6c48d07a 100644 --- a/config/example-config.yaml +++ b/config/example-config.yaml @@ -37,7 +37,8 @@ server: # The time window in which the maximum number of requests is allowed, i.e. the quota is reset after this time. # The time is given in seconds. window: 300 - # Use the list to exclude hosts from the request limit. You can specify single IP addresses as well as subnets. + # Use the list to exclude hosts from the request limit. You can specify single IP addresses, subnets and + # hostnames including wildcards. always_allow: - "127.0.0.1" @@ -123,7 +124,7 @@ features: # - exp # - scope # - audience - # - ip + # - hosts # - geoip_allow # - geoip_disallow # - usages_AT diff --git a/docker/example-docker-config.yaml b/docker/example-docker-config.yaml index 75b3e68c..9b35f334 100644 --- a/docker/example-docker-config.yaml +++ b/docker/example-docker-config.yaml @@ -26,6 +26,19 @@ server: key: # If behind a load balancer or reverse proxy, set this option. Mytoken will read the client's ip address from this header. proxy_header: "X-FORWARDED-FOR" + # Configure the request limits (these are per IP) + request_limits: + # Unless false request limits are enabled + enabled: true + # The number of requests that are allowed within the specified time window, e.g. 10/1s 100/5min 1000/1h + max_requests: 100 + # The time window in which the maximum number of requests is allowed, i.e. the quota is reset after this time. + # The time is given in seconds. + window: 300 + # Use the list to exclude hosts from the request limit. You can specify single IP addresses, subnets and + # hostnames including wildcards. +# always_allow: +# - "127.0.0.1" # The database file for ip geo location. Will be installed by setup to this location. geo_ip_db_file: "/mytoken/IP2LOCATION-LITE-DB1.IPV6.BIN" @@ -102,7 +115,7 @@ features: # - exp # - scope # - audience - # - ip + # - hosts # - geoip_allow # - geoip_disallow # - usages_AT diff --git a/go.mod b/go.mod index c13712a1..1722ce54 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,9 @@ require ( github.com/jinzhu/copier v0.3.5 github.com/jmoiron/sqlx v1.3.5 github.com/lestrrat-go/jwx v1.2.25 - github.com/oidc-mytoken/api v0.8.0 - github.com/oidc-mytoken/lib v0.5.0 + github.com/oidc-mytoken/api v0.9.1 + github.com/oidc-mytoken/lib v0.6.1 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pires/go-proxyproto v0.6.2 github.com/pkg/errors v0.9.1 github.com/satori/go.uuid v1.2.0 diff --git a/go.sum b/go.sum index 73ea072a..6f0e5e52 100644 --- a/go.sum +++ b/go.sum @@ -361,12 +361,15 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 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/oidc-mytoken/api v0.8.0 h1:V/8LyLcVtYX1xxj+r6KyShDYUhl1giHLPVB6dTZyQtk= -github.com/oidc-mytoken/api v0.8.0/go.mod h1:DBIlUbaIgGlf607VZx8zFC97VR3WNN0kaMVO1AqyTdE= -github.com/oidc-mytoken/lib v0.5.0 h1:/DHEjHCSVDFE5DXyXUp3dO7NWZfSFN2nYVaigYdmv5w= -github.com/oidc-mytoken/lib v0.5.0/go.mod h1:r6shfKy8/15vqGJvciCPphRkKffvIegSqj0uA6kzuns= +github.com/oidc-mytoken/api v0.9.0/go.mod h1:DBIlUbaIgGlf607VZx8zFC97VR3WNN0kaMVO1AqyTdE= +github.com/oidc-mytoken/api v0.9.1 h1:Hmv+KaGyxiodf92mXjELFeZOa9tBsQnUyEoBAFQNls8= +github.com/oidc-mytoken/api v0.9.1/go.mod h1:DBIlUbaIgGlf607VZx8zFC97VR3WNN0kaMVO1AqyTdE= +github.com/oidc-mytoken/lib v0.6.1 h1:ZtPj/1dc8rOf7VJlyCp3uBWEcEZJ7L2Ab50oI/ruoa4= +github.com/oidc-mytoken/lib v0.6.1/go.mod h1:AuKCljwZSdYfia6G6FvHl926pR/8ICyxrkbYKQgoGd4= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= diff --git a/internal/endpoints/consent/pkg/restriction.go b/internal/endpoints/consent/pkg/restriction.go index d44e25d3..9d4527fd 100644 --- a/internal/endpoints/consent/pkg/restriction.go +++ b/internal/endpoints/consent/pkg/restriction.go @@ -61,7 +61,7 @@ func (r WebRestrictions) getIPClass() bool { } ip := false for _, rr := range r.Restrictions { - if len(rr.IPs) > 0 { + if len(rr.Hosts) > 0 { ip = true break } diff --git a/internal/model/restrictionKey.go b/internal/model/restrictionKey.go index d762ddf5..58358810 100644 --- a/internal/model/restrictionKey.go +++ b/internal/model/restrictionKey.go @@ -32,7 +32,7 @@ const ( // assert that these are in the same order as api.AllRestrictionKeys RestrictionClaimExpiresAt RestrictionClaimScope RestrictionClaimAudiences - RestrictionClaimIPs + RestrictionClaimHosts RestrictionClaimGeoIPAllow RestrictionClaimGeoIPDisallow RestrictionClaimUsagesAT @@ -106,7 +106,7 @@ func (rc RestrictionClaim) MarshalJSON() ([]byte, error) { return data, errors.WithStack(err) } -// Has checks if a a RestrictionClaim is in a RestrictionClaims +// Has checks if a RestrictionClaim is in a RestrictionClaims func (rks RestrictionClaims) Has(rk RestrictionClaim) bool { for _, k := range rks { if k == rk { diff --git a/internal/server/middlerwares.go b/internal/server/middlerwares.go index 2d5d3564..169ee938 100644 --- a/internal/server/middlerwares.go +++ b/internal/server/middlerwares.go @@ -23,8 +23,8 @@ import ( "github.com/oidc-mytoken/server/internal/server/apiPath" "github.com/oidc-mytoken/server/internal/server/routes" "github.com/oidc-mytoken/server/internal/utils/fileio" + "github.com/oidc-mytoken/server/internal/utils/iputils" loggerUtils "github.com/oidc-mytoken/server/internal/utils/logger" - "github.com/oidc-mytoken/server/shared/utils" ) //go:embed web/static @@ -80,7 +80,7 @@ func addLimiterMiddleware(s fiber.Router) { limiter.New( limiter.Config{ Next: func(c *fiber.Ctx) bool { - return utils.IPIsIn(c.IP(), limiterConf.AlwaysAllow) + return iputils.IPIsIn(c.IP(), limiterConf.AlwaysAllow) }, Max: limiterConf.Max, Expiration: time.Duration(limiterConf.Window) * time.Second, diff --git a/internal/server/web/partials/restrictions-gui-editor.mustache b/internal/server/web/partials/restrictions-gui-editor.mustache index 0f09e2ce..ae9d88a5 100644 --- a/internal/server/web/partials/restrictions-gui-editor.mustache +++ b/internal/server/web/partials/restrictions-gui-editor.mustache @@ -82,20 +82,20 @@
-
- - +
+ +
- + - +
- If set, the mytoken can only be - used from these IP addresses or subnets. + If set, the mytoken can only be + used from these Hosts given by hostname, IP address or subnets.
diff --git a/internal/server/web/partials/restrictions.mustache b/internal/server/web/partials/restrictions.mustache index 86277045..36c5d741 100644 --- a/internal/server/web/partials/restrictions.mustache +++ b/internal/server/web/partials/restrictions.mustache @@ -18,7 +18,7 @@
- 0) { howManyClausesRestrictAud++; } - let ip = r['ip']; + let hosts = r['hosts'] || r['ip']; let ipW = r['geoip_allow']; let ipB = r['geoip_disallow']; - if ((ip !== undefined && ip.length > 0) || + if ((hosts !== undefined && hosts.length > 0) || (ipW !== undefined && ipW.length > 0) || (ipB !== undefined && ipB.length > 0)) { howManyClausesRestrictIP++; @@ -72,15 +72,16 @@ function updateRestrIcons(prefix = "") { } let restr = getRestrictionsData(prefix); if (howManyClausesRestrictIP === restr.length && restr.length > 0) { - restrIconIP(prefix).addClass('text-success'); - restrIconIP(prefix).removeClass('text-warning'); - restrIconIP(prefix).removeClass('text-danger'); - restrIconIP(prefix).attr('data-original-title', "The IPs from which this token can be used are restricted."); + restrIconHost(prefix).addClass('text-success'); + restrIconHost(prefix).removeClass('text-warning'); + restrIconHost(prefix).removeClass('text-danger'); + restrIconHost(prefix).attr('data-original-title', "The hosts from which this token can be used are" + + " restricted."); } else { - restrIconIP(prefix).addClass('text-warning'); - restrIconIP(prefix).removeClass('text-success'); - restrIconIP(prefix).removeClass('text-danger'); - restrIconIP(prefix).attr('data-original-title', "This token can be used from any IP."); + restrIconHost(prefix).addClass('text-warning'); + restrIconHost(prefix).removeClass('text-success'); + restrIconHost(prefix).removeClass('text-danger'); + restrIconHost(prefix).attr('data-original-title', "This token can be used from any host."); } if (howManyClausesRestrictScope === restr.length && restr.length > 0) { restrIconScope(prefix).addClass('text-success'); diff --git a/internal/utils/cache/cache.go b/internal/utils/cache/cache.go new file mode 100644 index 00000000..8ec49016 --- /dev/null +++ b/internal/utils/cache/cache.go @@ -0,0 +1,62 @@ +package cache + +import ( + "fmt" + "net" + "time" + + "github.com/patrickmn/go-cache" +) + +var c *cache.Cache + +func init() { + c = cache.New(5*time.Minute, 10*time.Minute) +} + +// Type defines the type of cache +type Type int + +// Different cache types +const ( + IPHostCache Type = iota + IPAddrCache + IPNetCache +) + +func k(t Type, key string) string { + return fmt.Sprintf("%d:%s", t, key) +} + +// Set sets a value in a cache +func Set(t Type, key string, value interface{}, expiration ...time.Duration) { + exp := cache.DefaultExpiration + if len(expiration) > 0 { + exp = expiration[0] + } + c.Set(k(t, key), value, exp) +} + +// Get returns the cached value for a given key +func Get(t Type, key string) (interface{}, bool) { + return c.Get(k(t, key)) +} + +// SetIPParseResult caches the result of an ip parsing +func SetIPParseResult(ipStr string, ip net.IP, ipNet *net.IPNet) { + Set(IPAddrCache, ipStr, ip) + Set(IPNetCache, ipStr, ipNet) +} + +// GetIPParseResult returns the caches result of an ip parsing +func GetIPParseResult(ipStr string) (net.IP, *net.IPNet, bool) { + ip, ipFound := Get(IPAddrCache, ipStr) + if !ipFound { + return nil, nil, false + } + ipNet, netFound := Get(IPNetCache, ipStr) + if !netFound { + return nil, nil, false + } + return ip.(net.IP), ipNet.(*net.IPNet), true +} diff --git a/internal/utils/iputils/iputils.go b/internal/utils/iputils/iputils.go new file mode 100644 index 00000000..c403acd4 --- /dev/null +++ b/internal/utils/iputils/iputils.go @@ -0,0 +1,165 @@ +package iputils + +import ( + "bytes" + "context" + "net" + "strings" + "time" + + "github.com/oidc-mytoken/server/internal/utils/cache" +) + +const hostLookupTimeout = 50 * time.Millisecond + +func getHost(ip string) string { + cacheHost, found := cache.Get(cache.IPHostCache, ip) + if found { + return cacheHost.(string) + } + ctx, cancel := context.WithTimeout(context.TODO(), hostLookupTimeout) + defer cancel() + r := net.Resolver{ + // PreferGo: true, + } + hosts, err := r.LookupAddr(ctx, ip) + if err != nil && len(hosts) < 1 { + return "" + } + host := hosts[0] + cache.Set(cache.IPHostCache, ip, host) + return host +} + +// IPsAreSubSet checks if all ips of ipsA are contained in ipsB, it will also check ip subnets +func IPsAreSubSet(ipsA, ipsB []string) bool { + for _, ipA := range ipsA { + if !IPIsIn(ipA, ipsB) { + return false + } + } + return true +} + +func parseIP(ip string) (net.IP, *net.IPNet) { + ipA, ipNet, found := cache.GetIPParseResult(ip) + if found { + return ipA, ipNet + } + var err error + ipA, ipNet, err = net.ParseCIDR(ip) + if err != nil { + ipA = net.ParseIP(ip) + } + if ipNet != nil && !ipA.Equal(ipNet.IP) { + ipNet = nil + } + cache.SetIPParseResult(ip, ipA, ipNet) + return ipA, ipNet +} + +// IPIsIn checks if an ip is in a slice of ip/hosts, it will also check ip subnets +func IPIsIn(ip string, ipOrHosts []string) bool { + for _, ipOrHost := range ipOrHosts { + if compareIPToIPOrHost(ip, ipOrHost) { + return true + } + } + return false +} + +func compareIPToIP(ip, ipp string) bool { + ipA, ipNetA := parseIP(ip) + ipB, ipNetB := parseIP(ipp) + if ipNetA == nil && ipNetB == nil { + if ipA.Equal(ipB) { + return true + } + } else if ipNetA == nil && ipNetB != nil { + if ipNetB.Contains(ipA) { + return true + } + } else if ipNetA != nil && ipNetB != nil { + if ipNetB.Contains(ipA) && bytes.Compare(ipNetA.Mask, ipNetB.Mask) >= 0 { + return true + } + } + // check for ipNetA != nil && ipNetB == nil not needed -> won't work + return false +} + +func compareIPToHost(ip, host string) bool { + ipHost := getHost(ip) + if ipHost == "" { + return false + } + if ipHost[len(ipHost)-1] == '.' && host[len(host)-1] != '.' { + host += "." + } + if len(host) > 1 && host[0] == '*' { + return strings.HasSuffix(ipHost, host[1:]) + } + return strings.Compare(ipHost, host) == 0 +} + +func ipValid(ip net.IP) bool { + return ip != nil && !ip.IsUnspecified() +} + +func compareIPToIPOrHost(ip, iphost string) bool { + ipA, _ := parseIP(ip) + ipHostA, _ := parseIP(iphost) + if ipValid(ipA) && !ipValid(ipHostA) { + return compareIPToHost(ip, iphost) + } + if ipValid(ipA) && ipValid(ipHostA) { + return compareIPToIP(ip, iphost) + } + if !ipValid(ipA) && !ipValid(ipHostA) { + return compareHostToHost(ip, iphost) + } + if !ipValid(ipA) && ipValid(ipHostA) { + return compareHostToIP(ip, iphost) + } + return false +} + +func compareHostToIP(host, ip string) bool { + if len(host) > 1 && host[0] == '*' { + return false + } + if len(host) > 0 && host[len(host)-1] != '.' { + host += "." + } + ipHost := getHost(ip) + if len(ipHost) > 0 && ipHost[len(ipHost)-1] != '.' { + ipHost += "." + } + return host == ipHost +} + +func compareHostToHost(a, b string) bool { + if len(a) > 0 && a[len(a)-1] != '.' { + a += "." + } + if len(b) > 0 && b[len(b)-1] != '.' { + b += "." + } + if a == b { + return true + } + if len(a) > 1 && a[0] == '*' { + if len(b) > 1 && b[0] != '*' { + return false + } + return strings.HasSuffix(a[1:], b[1:]) + } + if len(b) > 0 && b[0] != '*' { + // a!=b + // a[0]!='*' + // b[0]!='*' + // -> a really different from b + return false + } + return len(b) > 1 && strings.HasSuffix(a, b[1:]) +} diff --git a/shared/mytoken/restrictions/restriction.go b/shared/mytoken/restrictions/restriction.go index 4d208cd0..9f7b3df9 100644 --- a/shared/mytoken/restrictions/restriction.go +++ b/shared/mytoken/restrictions/restriction.go @@ -14,6 +14,7 @@ import ( "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/utils/errorfmt" + "github.com/oidc-mytoken/server/internal/utils/iputils" "github.com/oidc-mytoken/api/v0" @@ -59,8 +60,8 @@ func (r *Restrictions) ClearUnsupportedKeys() { if disabledRestrictionKeys().Has(model.RestrictionClaimAudiences) { rr.Audiences = nil } - if disabledRestrictionKeys().Has(model.RestrictionClaimIPs) { - rr.IPs = nil + if disabledRestrictionKeys().Has(model.RestrictionClaimHosts) { + rr.Hosts = nil } if disabledRestrictionKeys().Has(model.RestrictionClaimGeoIPAllow) { rr.GeoIPAllow = nil @@ -78,6 +79,37 @@ func (r *Restrictions) ClearUnsupportedKeys() { } } +// legacyHash returns the legacy hash of this restriction (using "ip" instead of "hosts" key +func (r *Restriction) legacyHash() ([]byte, error) { + type legacy struct { + NotBefore unixtime.UnixTime `json:"nbf,omitempty"` + ExpiresAt unixtime.UnixTime `json:"exp,omitempty"` + Scope string `json:"scope,omitempty"` + Audiences []string `json:"audience,omitempty"` + Hosts []string `json:"ip,omitempty"` + GeoIPAllow []string `json:"geoip_allow,omitempty"` + GeoIPDisallow []string `json:"geoip_disallow,omitempty"` + UsagesAT *int64 `json:"usages_AT,omitempty"` + UsagesOther *int64 `json:"usages_other,omitempty"` + } + l := legacy{ + NotBefore: r.NotBefore, + ExpiresAt: r.ExpiresAt, + Scope: r.Scope, + Audiences: r.Audiences, + Hosts: r.Hosts, + GeoIPAllow: r.GeoIPAllow, + GeoIPDisallow: r.GeoIPDisallow, + UsagesAT: r.UsagesAT, + UsagesOther: r.UsagesOther, + } + j, err := json.Marshal(l) + if err != nil { + return nil, errors.WithStack(err) + } + return hashUtils.SHA512(j), nil +} + // hash returns the hash of this restriction func (r *Restriction) hash() ([]byte, error) { j, err := json.Marshal(r) @@ -105,16 +137,16 @@ func (r *Restriction) verifyTimeBased(rlog log.Ext1FieldLogger) bool { now := unixtime.Now() return r.verifyNbf(now) && r.verifyExp(now) } -func (r *Restriction) verifyIPBased(rlog log.Ext1FieldLogger, ip string) bool { - return r.verifyIPs(rlog, ip) && r.verifyGeoIP(rlog, ip) +func (r *Restriction) verifyLocationBased(rlog log.Ext1FieldLogger, ip string) bool { + return r.verifyHosts(rlog, ip) && r.verifyGeoIP(rlog, ip) } -func (r *Restriction) verifyIPs(rlog log.Ext1FieldLogger, ip string) bool { - if disabledRestrictionKeys().Has(model.RestrictionClaimIPs) { +func (r *Restriction) verifyHosts(rlog log.Ext1FieldLogger, ip string) bool { + if disabledRestrictionKeys().Has(model.RestrictionClaimHosts) { return true } - rlog.Trace("Verifying ips") - return len(r.IPs) == 0 || - utils.IPIsIn(ip, r.IPs) + rlog.Trace("Verifying hosts") + return len(r.Hosts) == 0 || + iputils.IPIsIn(ip, r.Hosts) } func (r *Restriction) verifyGeoIP(rlog log.Ext1FieldLogger, ip string) bool { rlog.Trace("Verifying ip geo location") @@ -147,6 +179,14 @@ func (r *Restriction) getATUsageCounts(rlog log.Ext1FieldLogger, tx *sqlx.Tx, my if err != nil { return nil, err } + usages, err := mytokenrepohelper.GetTokenUsagesAT(rlog, tx, myID, string(hash)) + if err != nil || usages != nil { + return usages, err + } + hash, err = r.legacyHash() + if err != nil { + return nil, err + } return mytokenrepohelper.GetTokenUsagesAT(rlog, tx, myID, string(hash)) } func (r *Restriction) verifyATUsageCounts(rlog log.Ext1FieldLogger, tx *sqlx.Tx, myID mtid.MTID) bool { @@ -220,7 +260,7 @@ func (r *Restriction) verifyOtherUsageCounts(rlog log.Ext1FieldLogger, tx *sqlx. } func (r *Restriction) verify(rlog log.Ext1FieldLogger, ip string) bool { return r.verifyTimeBased(rlog) && - r.verifyIPBased(rlog, ip) + r.verifyLocationBased(rlog, ip) } func (r *Restriction) verifyAT(rlog log.Ext1FieldLogger, tx *sqlx.Tx, ip string, id mtid.MTID) bool { return r.verify(rlog, ip) && r.verifyATUsageCounts(rlog, tx, id) @@ -480,7 +520,7 @@ func Tighten(rlog log.Ext1FieldLogger, old, wanted Restrictions) (res Restrictio // ReplaceThisIp replaces the special value 'this' with the given ip. func (r *Restrictions) ReplaceThisIp(ip string) { for _, rr := range *r { - utils.ReplaceStringInSlice(&rr.IPs, "this", ip, false) + utils.ReplaceStringInSlice(&rr.Hosts, "this", ip, false) } } @@ -512,7 +552,7 @@ func (r *Restriction) isTighterThan(b *Restriction) bool { ) && len(b.Audiences) != 0 { return false } - if len(r.IPs) == 0 && len(b.IPs) > 0 || !utils.IPsAreSubSet(r.IPs, b.IPs) && len(b.IPs) != 0 { + if len(r.Hosts) == 0 && len(b.Hosts) > 0 || !iputils.IPsAreSubSet(r.Hosts, b.Hosts) && len(b.Hosts) != 0 { return false } if len(r.GeoIPAllow) == 0 && len(b.GeoIPAllow) > 0 || !utils.IsSubSet( diff --git a/shared/mytoken/restrictions/restriction_test.go b/shared/mytoken/restrictions/restriction_test.go index 2069b446..ccd30871 100644 --- a/shared/mytoken/restrictions/restriction_test.go +++ b/shared/mytoken/restrictions/restriction_test.go @@ -528,10 +528,10 @@ func TestRestriction_isTighterThan(t *testing.T) { }, { name: "IP", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, b: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.14", }, @@ -543,21 +543,21 @@ func TestRestriction_isTighterThan(t *testing.T) { name: "IP Reversed", a: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.14", }, }, }, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, expected: false, }, { name: "IP with explicit net", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12/24"}}}, b: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.14", }, @@ -569,33 +569,33 @@ func TestRestriction_isTighterThan(t *testing.T) { name: "IP with explicit net Reversed", a: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.14", }, }, }, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12/24"}}}, expected: false, }, { name: "IP Subnet", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, expected: true, }, { name: "IP Subnet Reversed", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, expected: false, }, { name: "IP Subnet + IP", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, b: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.0/24", "192.168.1.2", }, @@ -607,21 +607,21 @@ func TestRestriction_isTighterThan(t *testing.T) { name: "IP Subnet + IP Reversed", a: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.0/24", "192.168.1.2", }, }, }, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.12"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.12"}}}, expected: false, }, { name: "IP Subnets + IP", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, b: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.0/24", "192.168.1.2", }, @@ -633,49 +633,49 @@ func TestRestriction_isTighterThan(t *testing.T) { name: "IP Subnets + IP Reversed", a: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.0/24", "192.168.1.2", }, }, }, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, expected: false, }, { name: "IP Different sized Subnets", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, expected: true, }, { name: "IP Different sized Subnets Reversed", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/24"}}}, expected: false, }, { name: "IP Different sized Subnets 2", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.128.0/24"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.128.0/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, expected: true, }, { name: "IP Different sized Subnets 2 Reversed", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.128.0/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.128.0/24"}}}, expected: false, }, { name: "IP Different Subnets", - a: Restriction{Restriction: api.Restriction{IPs: []string{"193.168.0.0/24"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"193.168.0.0/24"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, expected: false, }, { name: "IP Different Subnets Reversed", - a: Restriction{Restriction: api.Restriction{IPs: []string{"192.168.0.0/16"}}}, - b: Restriction{Restriction: api.Restriction{IPs: []string{"193.168.0.0/24"}}}, + a: Restriction{Restriction: api.Restriction{Hosts: []string{"192.168.0.0/16"}}}, + b: Restriction{Restriction: api.Restriction{Hosts: []string{"193.168.0.0/24"}}}, expected: false, }, { @@ -683,7 +683,7 @@ func TestRestriction_isTighterThan(t *testing.T) { a: Restriction{}, b: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.42", }, @@ -695,7 +695,7 @@ func TestRestriction_isTighterThan(t *testing.T) { name: "IP B empty", a: Restriction{ Restriction: api.Restriction{ - IPs: []string{ + Hosts: []string{ "192.168.0.12", "192.168.0.42", }, @@ -704,6 +704,276 @@ func TestRestriction_isTighterThan(t *testing.T) { b: Restriction{}, expected: true, }, + { + name: "Hosts equal", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.example.com", + }, + }, + }, + expected: true, + }, + { + name: "Hosts equal wildcard", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + expected: true, + }, + { + name: "Hosts one wildcard", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + expected: true, + }, + { + name: "Hosts one wildcard reversed", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.example.com", + }, + }, + }, + expected: false, + }, + { + name: "Hosts two wildcards", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.test.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + expected: true, + }, + { + name: "Hosts two wildcards reversed", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.test.example.com", + }, + }, + }, + expected: false, + }, + { + name: "Hosts wildcard different", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.other.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.example.com", + }, + }, + }, + expected: false, + }, + { + name: "Host with ip", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "stackoverflow.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + expected: true, + }, + { + name: "Host with ip reversed", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "stackoverflow.com", + }, + }, + }, + expected: true, + }, + { + name: "wildcard Host with ip", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*stackoverflow.com", + }, + }, + }, + expected: true, + }, + { + name: "wildcard Host with ip", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*stackoverflow.com", + }, + }, + }, + expected: true, + }, + { + name: "wildcard Host with ip reversed", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.stackoverflow.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + expected: false, + }, + { + name: "wildcard Host 2 with ip", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.stackoverflow.com", + }, + }, + }, + expected: false, + }, + { + name: "wildcard Host 2 with ip reversed", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "*.stackoverflow.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "198.252.206.16", + }, + }, + }, + expected: false, + }, + { + name: "invalid ip for host", + a: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "test.example.com", + }, + }, + }, + b: Restriction{ + Restriction: api.Restriction{ + Hosts: []string{ + "192.168.0.42", + }, + }, + }, + expected: false, + }, { name: "GeoIP Allow Subset", a: Restriction{ @@ -1058,7 +1328,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1087,7 +1357,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1120,7 +1390,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1149,7 +1419,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1182,7 +1452,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1211,7 +1481,7 @@ func TestRestriction_isTighterThan(t *testing.T) { "b", "c", }, - IPs: []string{ + Hosts: []string{ "a", "b", "c", @@ -1360,18 +1630,70 @@ func TestRestriction_hash(t *testing.T) { NotBefore: 1599939600, ExpiresAt: 1599948600, Restriction: api.Restriction{ - IPs: []string{"192.168.0.31"}, + Hosts: []string{"192.168.0.31"}, UsagesAT: utils.NewInt64(11), }, } - hash, err := r.hash() - if err != nil { - t.Error(err) + cases := []struct { + name string + r Restriction + expHash string + }{ + { + name: "hash", + r: r, + expHash: "umVWTDmDgo2NLCgUWFtTn4G8PfjUZYTMxJ5QJGGo4ZNFOno76ggNdQcPhDsfWfYMy79n6oxnwiahbvHZUuI96w==", + }, } - expected := "BS3WfHbHNUiVU8sJ+F49H9+69HnFtfVDy2m22vBv588nZ0kGblVNxZEcrTN+5NUiRkM7W80N4VpPgwEZBZl+3g==" - if string(hash) != expected { - t.Errorf("hash '%s' does not match expected hash '%s'", hash, expected) + for _, c := range cases { + t.Run( + c.name, func(t *testing.T) { + hash, err := c.r.hash() + if err != nil { + t.Error(err) + } + if string(hash) != c.expHash { + t.Errorf("hash '%s' does not match expected hash '%s'", hash, c.expHash) + } + }, + ) + } +} + +func TestRestriction_legacyHash(t *testing.T) { + r := Restriction{ + NotBefore: 1599939600, + ExpiresAt: 1599948600, + Restriction: api.Restriction{ + Hosts: []string{"192.168.0.31"}, + UsagesAT: utils.NewInt64(11), + }, + } + + cases := []struct { + name string + r Restriction + expHash string + }{ + { + name: "legacy hash", + r: r, + expHash: "BS3WfHbHNUiVU8sJ+F49H9+69HnFtfVDy2m22vBv588nZ0kGblVNxZEcrTN+5NUiRkM7W80N4VpPgwEZBZl+3g==", + }, + } + for _, c := range cases { + t.Run( + c.name, func(t *testing.T) { + hash, err := c.r.legacyHash() + if err != nil { + t.Error(err) + } + if string(hash) != c.expHash { + t.Errorf("hash '%s' does not match expected hash '%s'", hash, c.expHash) + } + }, + ) } } diff --git a/shared/utils/utils.go b/shared/utils/utils.go index 37d0a5a6..68034030 100644 --- a/shared/utils/utils.go +++ b/shared/utils/utils.go @@ -1,11 +1,9 @@ package utils import ( - "bytes" "encoding/base64" "fmt" "math/rand" - "net" "strings" "time" "unsafe" @@ -90,53 +88,6 @@ func IsSubSet(a, b []string) bool { return true } -// IPsAreSubSet checks if all ips of ipsA are contained in ipsB, it will also check ip subnets -func IPsAreSubSet(ipsA, ipsB []string) bool { - for _, ipA := range ipsA { - if !IPIsIn(ipA, ipsB) { - return false - } - } - return true -} - -func parseIP(ip string) (net.IP, *net.IPNet) { - ipA, ipNet, err := net.ParseCIDR(ip) - if err != nil { - ipA = net.ParseIP(ip) - } - if ipNet != nil && !ipA.Equal(ipNet.IP) { - ipNet = nil - } - return ipA, ipNet -} - -// IPIsIn checks if a ip is in a slice of ips, it will also check ip subnets -func IPIsIn(ip string, ips []string) bool { - if len(ips) == 0 { - return false - } - ipA, ipNetA := parseIP(ip) - for _, ipp := range ips { - ipB, ipNetB := parseIP(ipp) - if ipNetA == nil && ipNetB == nil { - if ipA.Equal(ipB) { - return true - } - } else if ipNetA == nil && ipNetB != nil { - if ipNetB.Contains(ipA) { - return true - } - } else if ipNetA != nil && ipNetB != nil { - if ipNetB.Contains(ipA) && bytes.Compare(ipNetA.Mask, ipNetB.Mask) >= 0 { - return true - } - } - // check for ipNetA != nil && ipNetB == nil not needed -> won't work - } - return false -} - // CombineURLPath combines multiple parts of a url func CombineURLPath(p string, ps ...string) (r string) { r = p From 06395f8aac0bb550e3593f40ebc91a055bc868da Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 20 Oct 2022 15:36:52 +0200 Subject: [PATCH 05/55] bump dependencies; increase go version to 1.19 --- CHANGELOG.md | 7 +++++++ go.mod | 37 +++++++++++++++++++++++++++++++------ go.sum | 24 ++++++++++++++++-------- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fe9079a..09b94fb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,13 @@ - Fixed a bug in the web interface where the scope selection indicator for access tokens where not updated. +### Dependencies + +- Bump go version to 1.19 +- Bump golang.org/x/mod from 0.5.1 to 0.6.0 +- Bump github.com/gofiber/fiber/v2 from 2.37.1 to 2.38.1 +- Bump github.com/gofiber/helmet/v2 from 2.2.16 to 2.2.17 + ## mytoken 0.6.0 ### API diff --git a/go.mod b/go.mod index 1722ce54..5b4d526e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/oidc-mytoken/server -go 1.16 +go 1.19 require ( github.com/Songmu/prompter v0.5.1 @@ -9,8 +9,8 @@ require ( github.com/gliderlabs/ssh v0.3.5 github.com/go-resty/resty/v2 v2.7.0 github.com/go-sql-driver/mysql v1.6.0 - github.com/gofiber/fiber/v2 v2.37.1 - github.com/gofiber/helmet/v2 v2.2.16 + github.com/gofiber/fiber/v2 v2.38.1 + github.com/gofiber/helmet/v2 v2.2.17 github.com/gofiber/template v1.7.1 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/ip2location/ip2location-go v8.3.0+incompatible @@ -26,11 +26,36 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/urfave/cli/v2 v2.3.1-0.20211205195634-e8d81738896c github.com/valyala/fasthttp v1.40.0 - golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d - golang.org/x/mod v0.5.1 + golang.org/x/crypto v0.1.0 + golang.org/x/mod v0.6.0 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 - golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 + golang.org/x/term v0.1.0 gopkg.in/yaml.v3 v3.0.1 ) +require ( + github.com/andybalholm/brotli v1.0.4 // indirect + github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/cbroglie/mustache v1.4.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect + github.com/goccy/go-json v0.9.7 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/klauspost/compress v1.15.11 // indirect + github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect + github.com/lestrrat-go/blackmagic v1.0.0 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/iter v1.0.1 // indirect + github.com/lestrrat-go/option v1.0.0 // indirect + github.com/mattn/go-isatty v0.0.14 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/tcplisten v1.0.0 // indirect + golang.org/x/net v0.1.0 // indirect + golang.org/x/sys v0.1.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.28.0 // indirect + gopkg.in/square/go-jose.v2 v2.6.0 // indirect +) + replace github.com/urfave/cli/v2 => github.com/zachmann/cli/v2 v2.3.1-0.20211220102037-d619fd40a704 diff --git a/go.sum b/go.sum index 6f0e5e52..21f33a46 100644 --- a/go.sum +++ b/go.sum @@ -161,10 +161,13 @@ github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.37.0/go.mod h1:xm3pDGlfE1xqVKb77iH8weLU0FFoTeWeK3nbiYM2Nh0= -github.com/gofiber/fiber/v2 v2.37.1 h1:QK2032gjv0ulegpv/qlTEBoXQD3eFFzCHXcNN12UZCs= github.com/gofiber/fiber/v2 v2.37.1/go.mod h1:j3UslgQeJQP3mNhBxHnLLE8TPqA1Fd/lrl4gD25rRUY= +github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU= +github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= github.com/gofiber/helmet/v2 v2.2.16 h1:ssLTF6z6GuIN2USq23psTCMYWgzrNhrBoJVwgCY4b80= github.com/gofiber/helmet/v2 v2.2.16/go.mod h1:rJ/S3yuSzSQAQROSoHE4uqsB1oLUREDnRJScaoByHtM= +github.com/gofiber/helmet/v2 v2.2.17 h1:E2h629KUdsfg/LnxTyBW+NJVkKxnC4DXVIBRhjLeUG4= +github.com/gofiber/helmet/v2 v2.2.17/go.mod h1:jq6+tYGHI+wIZ1n8us/RyINZDOKwCubwxNKNNoCqhQ4= github.com/gofiber/template v1.7.1 h1:QCRChZA6UrLROgMbzCMKm4a1yqM/5S8RTBKYWZ9GfL4= github.com/gofiber/template v1.7.1/go.mod h1:l3ZOSp8yrMvROzqyh0QTCw7MHet/yLBzaRX+wsiw+gM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -301,8 +304,9 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.15.0 h1:xqfchp4whNFxn5A4XFyyYtitiWI8Hy5EW59jEwcyL6U= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= +github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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= @@ -473,8 +477,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d h1:3qF+Z8Hkrw9sOhrFHti9TlB1Hkac1x+DNRkv0XQiFjo= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= +golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -511,8 +516,9 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= +golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -565,8 +571,9 @@ golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b h1:ZmngSVLe/wycRns9MKikG9OWIEjGcGAkacif7oYQaUY= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -681,13 +688,14 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64 h1:UiNENfZ8gDvpiWw7IpOMQ27spWmThO1RwwdQVbJahJM= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From 271c9a966bdb31fedef1d3b34d73708acaf18314 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 20 Oct 2022 15:46:18 +0200 Subject: [PATCH 06/55] fix modifying by-value receiver --- internal/endpoints/consent/pkg/restriction.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/endpoints/consent/pkg/restriction.go b/internal/endpoints/consent/pkg/restriction.go index 9d4527fd..69b235b3 100644 --- a/internal/endpoints/consent/pkg/restriction.go +++ b/internal/endpoints/consent/pkg/restriction.go @@ -24,7 +24,7 @@ func (r WebRestrictions) Text() string { return string(data) } -func (r WebRestrictions) getTimeClass() int { +func (r *WebRestrictions) getTimeClass() int { if r.timeClass != nil { return *r.timeClass } @@ -42,7 +42,7 @@ func (r WebRestrictions) getTimeClass() int { return -1 } -func (r WebRestrictions) getScopeClass() bool { +func (r *WebRestrictions) getScopeClass() bool { if r.scopeClass != nil { return *r.scopeClass } @@ -55,7 +55,7 @@ func (r WebRestrictions) getScopeClass() bool { return s } -func (r WebRestrictions) getIPClass() bool { +func (r *WebRestrictions) getIPClass() bool { if r.ipClass != nil { return *r.ipClass } @@ -78,7 +78,7 @@ func (r WebRestrictions) getIPClass() bool { return ip } -func (r WebRestrictions) getAudClass() bool { +func (r *WebRestrictions) getAudClass() bool { if r.audClass != nil { return *r.audClass } @@ -91,7 +91,7 @@ func (r WebRestrictions) getAudClass() bool { return a } -func (r WebRestrictions) getUsageClass() bool { +func (r *WebRestrictions) getUsageClass() bool { if r.usagesClass != nil { return *r.usagesClass } From f59510d690f6b8440d55d751248d66457a4e1877 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 20 Oct 2022 16:15:18 +0200 Subject: [PATCH 07/55] [webinterface] add mytoken client icon for useragent --- internal/server/web/static/js/tokeninfo.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/server/web/static/js/tokeninfo.js b/internal/server/web/static/js/tokeninfo.js index 9066efee..208a6ea0 100644 --- a/internal/server/web/static/js/tokeninfo.js +++ b/internal/server/web/static/js/tokeninfo.js @@ -83,6 +83,9 @@ function userAgentToHTMLIcons(userAgent) { if (userAgent.startsWith("oidc-agent")) { return ``; } + if (userAgent.startsWith("mytoken")) { + return ``; + } let icons = FaUserAgent.faUserAgent(userAgent); return `${icons.browser.html}${icons.os.html}${icons.platform.html}`; } From 4ca1af76ba416ca5f2c032579aec53e7e1ef87bf Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 20 Oct 2022 16:19:17 +0200 Subject: [PATCH 08/55] debug dns reverse lookup --- internal/utils/iputils/iputils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/utils/iputils/iputils.go b/internal/utils/iputils/iputils.go index c403acd4..0ad33908 100644 --- a/internal/utils/iputils/iputils.go +++ b/internal/utils/iputils/iputils.go @@ -20,7 +20,7 @@ func getHost(ip string) string { ctx, cancel := context.WithTimeout(context.TODO(), hostLookupTimeout) defer cancel() r := net.Resolver{ - // PreferGo: true, + PreferGo: true, } hosts, err := r.LookupAddr(ctx, ip) if err != nil && len(hosts) < 1 { From 9a2835ecac5bd7db0c55ca5ac6a688eb11833ad1 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 21 Oct 2022 14:34:50 +0200 Subject: [PATCH 09/55] fix dns reverse lookup --- internal/utils/iputils/iputils.go | 59 ++++++++++++++++--------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/internal/utils/iputils/iputils.go b/internal/utils/iputils/iputils.go index 0ad33908..739eafa1 100644 --- a/internal/utils/iputils/iputils.go +++ b/internal/utils/iputils/iputils.go @@ -2,33 +2,23 @@ package iputils import ( "bytes" - "context" "net" "strings" - "time" "github.com/oidc-mytoken/server/internal/utils/cache" ) -const hostLookupTimeout = 50 * time.Millisecond - -func getHost(ip string) string { +func getHosts(ip string) []string { cacheHost, found := cache.Get(cache.IPHostCache, ip) if found { - return cacheHost.(string) - } - ctx, cancel := context.WithTimeout(context.TODO(), hostLookupTimeout) - defer cancel() - r := net.Resolver{ - PreferGo: true, + return cacheHost.([]string) } - hosts, err := r.LookupAddr(ctx, ip) - if err != nil && len(hosts) < 1 { - return "" + hosts, err := net.LookupAddr(ip) + if err != nil { + return nil } - host := hosts[0] - cache.Set(cache.IPHostCache, ip, host) - return host + cache.Set(cache.IPHostCache, ip, hosts) + return hosts } // IPsAreSubSet checks if all ips of ipsA are contained in ipsB, it will also check ip subnets @@ -89,17 +79,23 @@ func compareIPToIP(ip, ipp string) bool { } func compareIPToHost(ip, host string) bool { - ipHost := getHost(ip) - if ipHost == "" { + ipHosts := getHosts(ip) + if len(ipHosts) == 0 { return false } - if ipHost[len(ipHost)-1] == '.' && host[len(host)-1] != '.' { - host += "." - } - if len(host) > 1 && host[0] == '*' { - return strings.HasSuffix(ipHost, host[1:]) + for _, ipHost := range ipHosts { + if ipHost[len(ipHost)-1] == '.' && host[len(host)-1] != '.' { + host += "." + } + if len(host) > 1 && host[0] == '*' { + if strings.HasSuffix(ipHost, host[1:]) { + return true + } + } else if strings.Compare(ipHost, host) == 0 { + return true + } } - return strings.Compare(ipHost, host) == 0 + return false } func ipValid(ip net.IP) bool { @@ -131,11 +127,16 @@ func compareHostToIP(host, ip string) bool { if len(host) > 0 && host[len(host)-1] != '.' { host += "." } - ipHost := getHost(ip) - if len(ipHost) > 0 && ipHost[len(ipHost)-1] != '.' { - ipHost += "." + ipHosts := getHosts(ip) + for _, ipHost := range ipHosts { + if len(ipHost) > 0 && ipHost[len(ipHost)-1] != '.' { + ipHost += "." + } + if host == ipHost { + return true + } } - return host == ipHost + return false } func compareHostToHost(a, b string) bool { From 6841271948d623392d9912fe300ca230b3993b77 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 21 Oct 2022 14:52:44 +0200 Subject: [PATCH 10/55] fix code issues --- cmd/mytoken-server/mytoken-setup/setup.go | 29 +++++++++++++------ internal/db/cluster/cluster.go | 34 +++++++++++------------ internal/utils/logger/logger.go | 3 +- internal/utils/zipdownload/zipdownload.go | 4 +-- pkg/oauth2x/config.go | 4 +-- pkg/oauth2x/revoke.go | 4 +-- shared/utils/fileutil/fileutil.go | 5 +++- 7 files changed, 47 insertions(+), 36 deletions(-) diff --git a/cmd/mytoken-server/mytoken-setup/setup.go b/cmd/mytoken-server/mytoken-setup/setup.go index 6e4af2ce..97c38a99 100644 --- a/cmd/mytoken-server/mytoken-setup/setup.go +++ b/cmd/mytoken-server/mytoken-setup/setup.go @@ -3,7 +3,6 @@ package main import ( "embed" "fmt" - "io/ioutil" "os" "time" @@ -40,8 +39,12 @@ func (cred _rootDBCredentials) toDBConf() config.DBConf { var dbFlags = []cli.Flag{ &cli.StringFlag{ - Name: "user", - Aliases: []string{"u", "root-user", "db-user"}, + Name: "user", + Aliases: []string{ + "u", + "root-user", + "db-user", + }, Usage: "The username for the (root) user used for setting up the db", EnvVars: []string{"DB_USER"}, Value: "root", @@ -49,10 +52,18 @@ var dbFlags = []cli.Flag{ Placeholder: "ROOT", }, &cli.StringFlag{ - Name: "password", - Aliases: []string{"p", "pw", "db-password", "db-pw"}, - Usage: "The password for the (root) user used for setting up the db", - EnvVars: []string{"DB_PW", "DB_PASSWORD"}, + Name: "password", + Aliases: []string{ + "p", + "pw", + "db-password", + "db-pw", + }, + Usage: "The password for the (root) user used for setting up the db", + EnvVars: []string{ + "DB_PW", + "DB_PASSWORD", + }, Destination: &rootDBCredentials.Password, Placeholder: "PASSWORD", }, @@ -162,7 +173,7 @@ func installGEOIPDB(_ *cli.Context) error { return err } log.Debug("Downloaded zip file") - err = ioutil.WriteFile(config.Get().GeoIPDBFile, archive["IP2LOCATION-LITE-DB1.IPV6.BIN"], 0644) + err = os.WriteFile(config.Get().GeoIPDBFile, archive["IP2LOCATION-LITE-DB1.IPV6.BIN"], 0600) if err == nil { log.WithField("file", config.Get().GeoIPDBFile).Debug("Installed geo ip database") fmt.Printf("Installed geo ip database file to '%s'.\n", config.Get().GeoIPDBFile) @@ -185,7 +196,7 @@ func createSigningKey(_ *cli.Context) error { os.Exit(1) } } - if err = ioutil.WriteFile(sigKeyFile, []byte(str), 0600); err != nil { + if err = os.WriteFile(sigKeyFile, []byte(str), 0600); err != nil { return err } log.WithField("filepath", sigKeyFile).Debug("Wrote key to file") diff --git a/internal/db/cluster/cluster.go b/internal/db/cluster/cluster.go index ea9a67b5..3570998d 100644 --- a/internal/db/cluster/cluster.go +++ b/internal/db/cluster/cluster.go @@ -20,7 +20,7 @@ import ( func NewFromConfig(conf config.DBConf) *Cluster { c := newCluster(len(conf.Hosts)) c.conf = &conf - c.startReconnector() + go c.runReconnector() c.AddNodes() log.Debug("Created db cluster") return c @@ -93,27 +93,25 @@ func (c *Cluster) addNode(n *node) error { return nil } -func (c *Cluster) startReconnector() { - go func() { - for { - select { - case <-c.stop: +func (c *Cluster) runReconnector() { + for { + select { + case <-c.stop: + log.Debug("Stopping re-connector") + return + default: + log.Debug("Run checkNodesDown") + if c.checkNodesDown() { log.Debug("Stopping re-connector") return - default: - log.Debug("Run checkNodesDown") - if c.checkNodesDown() { - log.Debug("Stopping re-connector") - return - } - conf := c.conf - if conf == nil { - conf = &config.Get().DB - } - time.Sleep(time.Duration(conf.ReconnectInterval) * time.Second) } + conf := c.conf + if conf == nil { + conf = &config.Get().DB + } + time.Sleep(time.Duration(conf.ReconnectInterval) * time.Second) } - }() + } } func (c *Cluster) checkNodesDown() bool { diff --git a/internal/utils/logger/logger.go b/internal/utils/logger/logger.go index d02e4cab..c3759d38 100644 --- a/internal/utils/logger/logger.go +++ b/internal/utils/logger/logger.go @@ -2,7 +2,6 @@ package logger import ( "io" - "io/ioutil" "os" "path/filepath" @@ -108,7 +107,7 @@ func SetOutput() { LogLevels: minLogLevelToLevels(logLevel), }, ) - log.SetOutput(ioutil.Discard) + log.SetOutput(io.Discard) } func minLogLevelToLevels(minLevel log.Level) (levels []log.Level) { diff --git a/internal/utils/zipdownload/zipdownload.go b/internal/utils/zipdownload/zipdownload.go index dee16902..72ed6ee9 100644 --- a/internal/utils/zipdownload/zipdownload.go +++ b/internal/utils/zipdownload/zipdownload.go @@ -3,7 +3,7 @@ package zipdownload import ( "archive/zip" "bytes" - "io/ioutil" + "io" "github.com/pkg/errors" @@ -41,6 +41,6 @@ func readZipFile(zf *zip.File) ([]byte, error) { return nil, errors.WithStack(err) } defer f.Close() - data, err := ioutil.ReadAll(f) + data, err := io.ReadAll(f) return data, errors.WithStack(err) } diff --git a/pkg/oauth2x/config.go b/pkg/oauth2x/config.go index c7889c19..e4ed7352 100644 --- a/pkg/oauth2x/config.go +++ b/pkg/oauth2x/config.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" "fmt" - "io/ioutil" + "io" "net/http" "strings" @@ -68,7 +68,7 @@ func (c *Config) discovery() error { } defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("unable to read response body: %v", err) } diff --git a/pkg/oauth2x/revoke.go b/pkg/oauth2x/revoke.go index 3939a2fe..74d098ac 100644 --- a/pkg/oauth2x/revoke.go +++ b/pkg/oauth2x/revoke.go @@ -2,7 +2,7 @@ package oauth2x import ( "fmt" - "io/ioutil" + "io" "net/http" "strings" @@ -24,7 +24,7 @@ func (c *Config) RevokeToken(oauth2Config *oauth2.Config, token string) error { defer resp.Body.Close() if resp.StatusCode != http.StatusOK { - body, err := ioutil.ReadAll(resp.Body) + body, err := io.ReadAll(resp.Body) if err != nil { return fmt.Errorf("unable to read response body: %v", err) } diff --git a/shared/utils/fileutil/fileutil.go b/shared/utils/fileutil/fileutil.go index 40a5ac9d..799f5c82 100644 --- a/shared/utils/fileutil/fileutil.go +++ b/shared/utils/fileutil/fileutil.go @@ -49,7 +49,10 @@ func Append(path, content string, doNotCreateIfDoesNotExist ...bool) error { } defer f.Close() _, err = f.WriteString(content) - return err + if err != nil { + return err + } + return f.Sync() } // ReadFile reads a given file and returns the content. From c64ccc94f99c6f0af997ed37485b0b577ecf927d Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 21 Oct 2022 15:02:31 +0200 Subject: [PATCH 11/55] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b94fb3..6ecbcf64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,7 @@ ### Enhancements - Location restriction can now be done with host names, not only plain ip addresses, see above for more details. +- Improved code quality ### Bugfixes From 3915d0d77acebd6b0a366da898c65d96edda87a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Oct 2022 03:12:58 +0000 Subject: [PATCH 12/55] Bump github.com/gofiber/fiber/v2 from 2.38.1 to 2.39.0 Bumps [github.com/gofiber/fiber/v2](https://github.com/gofiber/fiber) from 2.38.1 to 2.39.0. - [Release notes](https://github.com/gofiber/fiber/releases) - [Commits](https://github.com/gofiber/fiber/compare/v2.38.1...v2.39.0) --- updated-dependencies: - dependency-name: github.com/gofiber/fiber/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 7 +++++-- go.sum | 17 +++++++++++------ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 5b4d526e..4ef7048d 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/gliderlabs/ssh v0.3.5 github.com/go-resty/resty/v2 v2.7.0 github.com/go-sql-driver/mysql v1.6.0 - github.com/gofiber/fiber/v2 v2.38.1 + github.com/gofiber/fiber/v2 v2.39.0 github.com/gofiber/helmet/v2 v2.2.17 github.com/gofiber/template v1.7.1 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -47,7 +47,10 @@ require ( github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/iter v1.0.1 // indirect github.com/lestrrat-go/option v1.0.0 // indirect - github.com/mattn/go-isatty v0.0.14 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/rivo/uniseg v0.2.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect diff --git a/go.sum b/go.sum index 21f33a46..27e8116a 100644 --- a/go.sum +++ b/go.sum @@ -160,12 +160,10 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gofiber/fiber/v2 v2.37.0/go.mod h1:xm3pDGlfE1xqVKb77iH8weLU0FFoTeWeK3nbiYM2Nh0= github.com/gofiber/fiber/v2 v2.37.1/go.mod h1:j3UslgQeJQP3mNhBxHnLLE8TPqA1Fd/lrl4gD25rRUY= -github.com/gofiber/fiber/v2 v2.38.1 h1:GEQ/Yt3Wsf2a30iTqtLXlBYJZso0JXPovt/tmj5H9jU= github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= -github.com/gofiber/helmet/v2 v2.2.16 h1:ssLTF6z6GuIN2USq23psTCMYWgzrNhrBoJVwgCY4b80= -github.com/gofiber/helmet/v2 v2.2.16/go.mod h1:rJ/S3yuSzSQAQROSoHE4uqsB1oLUREDnRJScaoByHtM= +github.com/gofiber/fiber/v2 v2.39.0 h1:uhWpYQ6EHN8J7FOPYbI2hrdBD/KNZBC5CjbuOd4QUt4= +github.com/gofiber/fiber/v2 v2.39.0/go.mod h1:Cmuu+elPYGqlvQvdKyjtYsjGMi69PDp8a1AY2I5B2gM= github.com/gofiber/helmet/v2 v2.2.17 h1:E2h629KUdsfg/LnxTyBW+NJVkKxnC4DXVIBRhjLeUG4= github.com/gofiber/helmet/v2 v2.2.17/go.mod h1:jq6+tYGHI+wIZ1n8us/RyINZDOKwCubwxNKNNoCqhQ4= github.com/gofiber/template v1.7.1 h1:QCRChZA6UrLROgMbzCMKm4a1yqM/5S8RTBKYWZ9GfL4= @@ -337,13 +335,18 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-slim v0.0.0-20200618151855-bde33eecb5ee/go.mod h1:ma9TUJeni8LGZMJvOwbAv/FOwiwqIMQN570LnpqCBSM= github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= @@ -398,6 +401,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 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/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -437,7 +442,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.39.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= @@ -688,6 +692,7 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= From f5808fd567d3e42428912669c639a47e466eb09d Mon Sep 17 00:00:00 2001 From: zachmann Date: Mon, 24 Oct 2022 09:44:32 +0200 Subject: [PATCH 13/55] adapt dev versioning --- CHANGELOG.md | 2 +- internal/model/version/version.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ecbcf64..ff4baf39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ -## mytoken 0.6.1 +## mytoken 0.6.1-a ### API diff --git a/internal/model/version/version.go b/internal/model/version/version.go index 0e5d19f0..fe393f4f 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -9,15 +9,15 @@ const ( MAJOR = 0 MINOR = 6 FIX = 1 - DEV = false + DEV = "a" ) var version = fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, FIX) -var devVersion = fmt.Sprintf("%s-dev", version) +var devVersion = fmt.Sprintf("%s-%s", version, DEV) // VERSION returns the current mytoken version func VERSION() string { - if DEV { + if DEV != "" { return devVersion } return version From 7429ae9c5167cb1099cb6ee6badecfa155eabe69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Oct 2022 03:13:17 +0000 Subject: [PATCH 14/55] Bump github.com/gofiber/helmet/v2 from 2.2.17 to 2.2.18 Bumps [github.com/gofiber/helmet/v2](https://github.com/gofiber/helmet) from 2.2.17 to 2.2.18. - [Release notes](https://github.com/gofiber/helmet/releases) - [Commits](https://github.com/gofiber/helmet/compare/v2.2.17...v2.2.18) --- updated-dependencies: - dependency-name: github.com/gofiber/helmet/v2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 4ef7048d..7d848fec 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/go-resty/resty/v2 v2.7.0 github.com/go-sql-driver/mysql v1.6.0 github.com/gofiber/fiber/v2 v2.39.0 - github.com/gofiber/helmet/v2 v2.2.17 + github.com/gofiber/helmet/v2 v2.2.18 github.com/gofiber/template v1.7.1 github.com/golang-jwt/jwt v3.2.2+incompatible github.com/ip2location/ip2location-go v8.3.0+incompatible diff --git a/go.sum b/go.sum index 27e8116a..9597f49e 100644 --- a/go.sum +++ b/go.sum @@ -161,11 +161,10 @@ github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofiber/fiber/v2 v2.37.1/go.mod h1:j3UslgQeJQP3mNhBxHnLLE8TPqA1Fd/lrl4gD25rRUY= -github.com/gofiber/fiber/v2 v2.38.1/go.mod h1:t0NlbaXzuGH7I+7M4paE848fNWInZ7mfxI/Er1fTth8= github.com/gofiber/fiber/v2 v2.39.0 h1:uhWpYQ6EHN8J7FOPYbI2hrdBD/KNZBC5CjbuOd4QUt4= github.com/gofiber/fiber/v2 v2.39.0/go.mod h1:Cmuu+elPYGqlvQvdKyjtYsjGMi69PDp8a1AY2I5B2gM= -github.com/gofiber/helmet/v2 v2.2.17 h1:E2h629KUdsfg/LnxTyBW+NJVkKxnC4DXVIBRhjLeUG4= -github.com/gofiber/helmet/v2 v2.2.17/go.mod h1:jq6+tYGHI+wIZ1n8us/RyINZDOKwCubwxNKNNoCqhQ4= +github.com/gofiber/helmet/v2 v2.2.18 h1:N6LcowwRpwhjLJZVEWTf3c/MjNKGdQSJffMUp02q1z0= +github.com/gofiber/helmet/v2 v2.2.18/go.mod h1:hGsMgmaBpCHX2GZlPXW7rghCFrGR0gXWfhsTljGQjhQ= github.com/gofiber/template v1.7.1 h1:QCRChZA6UrLROgMbzCMKm4a1yqM/5S8RTBKYWZ9GfL4= github.com/gofiber/template v1.7.1/go.mod h1:l3ZOSp8yrMvROzqyh0QTCw7MHet/yLBzaRX+wsiw+gM= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= From d9ac0262a519c50e26dc97ed0c7c6554a7437a78 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 26 Oct 2022 03:17:35 +0000 Subject: [PATCH 15/55] Bump github.com/valyala/fasthttp from 1.40.0 to 1.41.0 Bumps [github.com/valyala/fasthttp](https://github.com/valyala/fasthttp) from 1.40.0 to 1.41.0. - [Release notes](https://github.com/valyala/fasthttp/releases) - [Commits](https://github.com/valyala/fasthttp/compare/v1.40.0...v1.41.0) --- updated-dependencies: - dependency-name: github.com/valyala/fasthttp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 7d848fec..d68432d6 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/satori/go.uuid v1.2.0 github.com/sirupsen/logrus v1.9.0 github.com/urfave/cli/v2 v2.3.1-0.20211205195634-e8d81738896c - github.com/valyala/fasthttp v1.40.0 + github.com/valyala/fasthttp v1.41.0 golang.org/x/crypto v0.1.0 golang.org/x/mod v0.6.0 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 diff --git a/go.sum b/go.sum index 9597f49e..e0e4dcee 100644 --- a/go.sum +++ b/go.sum @@ -302,6 +302,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.11 h1:Lcadnb3RKGin4FYM/orgq0qde+nc15E5Cbqg4B9Sx9c= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -441,8 +442,9 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69 github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.40.0 h1:CRq/00MfruPGFLTQKY8b+8SfdK60TxNztjRMnH0t1Yc= github.com/valyala/fasthttp v1.40.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I= +github.com/valyala/fasthttp v1.41.0 h1:zeR0Z1my1wDHTRiamBCXVglQdbUwgb9uWG3k1HQz6jY= +github.com/valyala/fasthttp v1.41.0/go.mod h1:f6VbjjoI3z1NDOZOv17o6RvtRSWxC77seBFc2uWtgiY= github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0= @@ -575,6 +577,7 @@ golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= From 1f16dba002b338280a8a6edaa0d5b0a862ae57ec Mon Sep 17 00:00:00 2001 From: zachmann Date: Mon, 24 Oct 2022 11:00:43 +0200 Subject: [PATCH 16/55] adapt goreleaser to new ci plans --- .goreleaser-release.yml | 182 ++++++++++++++++++++++++++++++++++++++++ .goreleaser.yml | 28 +++---- 2 files changed, 196 insertions(+), 14 deletions(-) create mode 100644 .goreleaser-release.yml diff --git a/.goreleaser-release.yml b/.goreleaser-release.yml new file mode 100644 index 00000000..18192fcd --- /dev/null +++ b/.goreleaser-release.yml @@ -0,0 +1,182 @@ +project_name: mytoken-server +before: + hooks: + - go mod tidy +builds: + - id: server + main: ./cmd/mytoken-server + binary: mytoken-server + env: + - CGO_ENABLED=0 + goos: + - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' + - id: setup + main: ./cmd/mytoken-server/mytoken-setup + binary: mytoken-setup + env: + - CGO_ENABLED=0 + goos: + - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' + - id: migratedb + main: ./cmd/mytoken-server/mytoken-migratedb + binary: mytoken-migratedb + env: + - CGO_ENABLED=0 + goos: + - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' +archives: + - replacements: + darwin: macOS + 386: 32-bit + amd64: 64-bit + format_overrides: + - goos: windows + format: zip +nfpms: + - id: server-pkg + package_name: mytoken-server + replacements: + 386: i386 + file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" + builds: + - server + homepage: https://mytoken-doc.data.kit.edu/server/intro + maintainer: Gabriel Zachmann + description: Mytoken is a central web service with the goal to easily obtain OpenID Connect access tokens across devices. + license: MIT + formats: + - deb + - rpm + release: 1 + section: misc + bindir: /usr/bin + contents: + - src: config/example-config.yaml + dst: /etc/mytoken/example-config.yaml + type: config + - src: config/mytoken.service + dst: /etc/systemd/system/mytoken.service + - dst: /var/log/mytoken + type: dir + overrides: + rpm: + replacements: + amd64: x86_64 + file_name_template: "{{ .PackageName }}-{{ .Version }}.{{ .Arch }}" + - id: setup-pkg + package_name: mytoken-server-setup + replacements: + 386: i386 + file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" + builds: + - setup + homepage: https://mytoken-doc.data.kit.edu/server/intro + maintainer: Gabriel Zachmann + description: A setup utility for the mytoken-server + license: MIT + formats: + - deb + - rpm + release: 1 + section: misc + bindir: /usr/bin + overrides: + rpm: + replacements: + amd64: x86_64 + file_name_template: "{{ .PackageName }}-{{ .Version }}.{{ .Arch }}" + - id: migratedb-pkg + package_name: mytoken-server-migratedb + replacements: + 386: i386 + file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" + builds: + - migratedb + homepage: https://mytoken-doc.data.kit.edu/server/intro + maintainer: Gabriel Zachmann + description: A tool for migrating the database between versions + license: MIT + dependencies: + - mariadb-client + formats: + - deb + - rpm + release: 1 + section: misc + bindir: /usr/bin + overrides: + rpm: + replacements: + amd64: x86_64 + file_name_template: "{{ .PackageName }}-{{ .Version }}.{{ .Arch }}" +dockers: + - goos: linux + goarch: amd64 + ids: + - server + image_templates: + - "oidcmytoken/mytoken-server:latest" + - "oidcmytoken/mytoken-server:{{ .Tag }}" + - "oidcmytoken/mytoken-server:v{{ .Major }}" + - "oidcmytoken/mytoken-server:v{{ .Major }}.{{ .Minor }}" + dockerfile: cmd/mytoken-server/Dockerfile + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title=mytoken-server" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - goos: linux + goarch: amd64 + ids: + - setup + image_templates: + - "oidcmytoken/mytoken-setup:latest" + - "oidcmytoken/mytoken-setup:{{ .Tag }}" + - "oidcmytoken/mytoken-setup:v{{ .Major }}" + - "oidcmytoken/mytoken-setup:v{{ .Major }}.{{ .Minor }}" + dockerfile: cmd/mytoken-server/mytoken-setup/Dockerfile + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title=mytoken-setup" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" + - goos: linux + goarch: amd64 + ids: + - migratedb + image_templates: + - "oidcmytoken/mytoken-migratedb:latest" + - "oidcmytoken/mytoken-migratedb:{{ .Tag }}" + - "oidcmytoken/mytoken-migratedb:v{{ .Major }}" + - "oidcmytoken/mytoken-migratedb:v{{ .Major }}.{{ .Minor }}" + dockerfile: cmd/mytoken-server/mytoken-migratedb/Dockerfile + build_flag_templates: + - "--pull" + - "--label=org.opencontainers.image.created={{.Date}}" + - "--label=org.opencontainers.image.title=mytoken-migratedb" + - "--label=org.opencontainers.image.revision={{.FullCommit}}" + - "--label=org.opencontainers.image.version={{.Version}}" +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +release: + draft: true + name_template: "{{.ProjectName}} {{.Version}}" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' diff --git a/.goreleaser.yml b/.goreleaser.yml index 60c69025..1d8dd279 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -10,6 +10,9 @@ builds: - CGO_ENABLED=0 goos: - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' - id: setup main: ./cmd/mytoken-server/mytoken-setup binary: mytoken-setup @@ -17,6 +20,9 @@ builds: - CGO_ENABLED=0 goos: - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' - id: migratedb main: ./cmd/mytoken-server/mytoken-migratedb binary: mytoken-migratedb @@ -24,6 +30,9 @@ builds: - CGO_ENABLED=0 goos: - linux + gcflags: + - all=-trimpath={{.Env.GOPATH}} + mod_timestamp: '{{ .CommitTimestamp }}' archives: - replacements: @@ -116,11 +125,8 @@ dockers: ids: - server image_templates: - - "oidcmytoken/mytoken-server:latest" + - "oidcmytoken/mytoken-server:unstable" - "oidcmytoken/mytoken-server:{{ .Tag }}" - - "oidcmytoken/mytoken-server:v{{ .Major }}" - - "oidcmytoken/mytoken-server:v{{ .Major }}.{{ .Minor }}" -# skip_push: true dockerfile: cmd/mytoken-server/Dockerfile build_flag_templates: - "--pull" @@ -133,11 +139,8 @@ dockers: ids: - setup image_templates: - - "oidcmytoken/mytoken-setup:latest" + - "oidcmytoken/mytoken-setup:unstable" - "oidcmytoken/mytoken-setup:{{ .Tag }}" - - "oidcmytoken/mytoken-setup:v{{ .Major }}" - - "oidcmytoken/mytoken-setup:v{{ .Major }}.{{ .Minor }}" -# skip_push: true dockerfile: cmd/mytoken-server/mytoken-setup/Dockerfile build_flag_templates: - "--pull" @@ -150,12 +153,8 @@ dockers: ids: - migratedb image_templates: - - "oidcmytoken/mytoken-migratedb:latest" + - "oidcmytoken/mytoken-migratedb:unstable" - "oidcmytoken/mytoken-migratedb:{{ .Tag }}" - - "oidcmytoken/mytoken-migratedb:v{{ .Major }}" - - "oidcmytoken/mytoken-migratedb:v{{ .Major }}.{{ .Minor }}" -# skip_push: true - dockerfile: cmd/mytoken-server/mytoken-migratedb/Dockerfile build_flag_templates: - "--pull" - "--label=org.opencontainers.image.created={{.Date}}" @@ -167,7 +166,8 @@ checksum: snapshot: name_template: "{{ .Tag }}-next" release: - draft: true + disable: true + prerelease: auto name_template: "{{.ProjectName}} {{.Version}}" changelog: sort: asc From ef548b955170972d22d81066d7a44d802589ab6c Mon Sep 17 00:00:00 2001 From: zachmann Date: Mon, 24 Oct 2022 14:29:50 +0200 Subject: [PATCH 17/55] add gitlab ci --- .gitlab-ci-script.sh | 17 +++++++++++++++++ .gitlab-ci.yml | 15 +++++++++++++++ .goreleaser-release.yml | 1 + .goreleaser.yml | 12 +++++++++--- 4 files changed, 42 insertions(+), 3 deletions(-) create mode 100755 .gitlab-ci-script.sh create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci-script.sh b/.gitlab-ci-script.sh new file mode 100755 index 00000000..67ca5d9f --- /dev/null +++ b/.gitlab-ci-script.sh @@ -0,0 +1,17 @@ +set -x +mkdir ../shared +first=$(grep '^## ' -nm1 CHANGELOG.md | cut -d':' -f1); \ + second=$(grep '^## ' -nm2 CHANGELOG.md | tail -n1 | cut -d':' -f1); \ + tail -n+$first CHANGELOG.md | head -n$(($second-$first)) > ../shared/release.md +export GORELEASER_CONFIG="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '.goreleaser.yml'; else echo '.goreleaser-release.yml'; fi)" +BASEDIR=/go/src/github.com/oidc-mytoken/server +ls -l "${PWD}/../shared" +docker run --rm --privileged \ + -v $PWD:$BASEDIR \ + -w $BASEDIR\ + -v "${PWD}/../shared":/tmp/shared \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -e DOCKER_USERNAME -e DOCKER_PASSWORD \ + -e GITHUB_TOKEN \ + -e GORELEASER_CONFIG \ + goreleaser/goreleaser release -f $GORELEASER_CONFIG --release-notes /tmp/shared/release.md diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..1ed09ab2 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,15 @@ +prerelease: + stage: build + image: + name: docker:stable + services: + - docker:dind + only: + - tags + tags: + - linux + variables: + GIT_DEPTH: 0 + # DOCKER_REGISTRY: https://registry.hub.docker.com/v1/ + script: + - ./.gitlab-ci-script.sh diff --git a/.goreleaser-release.yml b/.goreleaser-release.yml index 18192fcd..a2b1c0cc 100644 --- a/.goreleaser-release.yml +++ b/.goreleaser-release.yml @@ -2,6 +2,7 @@ project_name: mytoken-server before: hooks: - go mod tidy +dist: results builds: - id: server main: ./cmd/mytoken-server diff --git a/.goreleaser.yml b/.goreleaser.yml index 1d8dd279..b70a089b 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -2,6 +2,7 @@ project_name: mytoken-server before: hooks: - go mod tidy +dist: results builds: - id: server main: ./cmd/mytoken-server @@ -10,8 +11,8 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' - id: setup main: ./cmd/mytoken-server/mytoken-setup @@ -155,6 +156,7 @@ dockers: image_templates: - "oidcmytoken/mytoken-migratedb:unstable" - "oidcmytoken/mytoken-migratedb:{{ .Tag }}" + dockerfile: cmd/mytoken-server/mytoken-migratedb/Dockerfile build_flag_templates: - "--pull" - "--label=org.opencontainers.image.created={{.Date}}" @@ -166,8 +168,12 @@ checksum: snapshot: name_template: "{{ .Tag }}-next" release: - disable: true + # disable: true prerelease: auto + draft: true + github: + owner: oidc-mytoken + name: server name_template: "{{.ProjectName}} {{.Version}}" changelog: sort: asc From 9f10229b6763e0a4758235e52499cb1ee7cfdf98 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 27 Oct 2022 08:28:29 +0200 Subject: [PATCH 18/55] add more ci actions --- .gitlab-ci-script.sh | 5 ++--- .gitlab-ci.yml | 52 +++++++++++++++++++++++++++++++++++++++++++- .goreleaser.yml | 29 ++++++++++++------------ 3 files changed, 67 insertions(+), 19 deletions(-) diff --git a/.gitlab-ci-script.sh b/.gitlab-ci-script.sh index 67ca5d9f..38e47bb9 100755 --- a/.gitlab-ci-script.sh +++ b/.gitlab-ci-script.sh @@ -1,11 +1,9 @@ -set -x mkdir ../shared first=$(grep '^## ' -nm1 CHANGELOG.md | cut -d':' -f1); \ second=$(grep '^## ' -nm2 CHANGELOG.md | tail -n1 | cut -d':' -f1); \ tail -n+$first CHANGELOG.md | head -n$(($second-$first)) > ../shared/release.md -export GORELEASER_CONFIG="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '.goreleaser.yml'; else echo '.goreleaser-release.yml'; fi)" +GORELEASER_CONFIG="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '.goreleaser.yml'; else echo '.goreleaser-release.yml'; fi)" BASEDIR=/go/src/github.com/oidc-mytoken/server -ls -l "${PWD}/../shared" docker run --rm --privileged \ -v $PWD:$BASEDIR \ -w $BASEDIR\ @@ -15,3 +13,4 @@ docker run --rm --privileged \ -e GITHUB_TOKEN \ -e GORELEASER_CONFIG \ goreleaser/goreleaser release -f $GORELEASER_CONFIG --release-notes /tmp/shared/release.md +ls -l results \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ed09ab2..18c107d5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,53 @@ -prerelease: +image: golang:1.19 +stages: + - build + - test + - lint + - release + +default: + tags: + - linux + +test: + stage: test + script: + - go test -v ./... + +test_race: + stage: test + script: + - go test -race -v ./... + +lint: + stage: lint + before_script: + - go install golang.org/x/lint/golint + script: + - golint -set_exit_status ./... + +vet: + stage: lint + script: + - go vet ./... + +build_server: stage: build + script: + - go build github.com/oidc-mytoken/server/cmd/mytoken-server + +build_setup: + stage: build + script: + - go build github.com/oidc-mytoken/server/cmd/mytoken-server/mytoken-setup + +build_migratedb: + stage: build + script: + - go build github.com/oidc-mytoken/server/cmd/mytoken-server/mytoken-migratedb + +prerelease: + stage: release image: name: docker:stable services: @@ -13,3 +61,5 @@ prerelease: # DOCKER_REGISTRY: https://registry.hub.docker.com/v1/ script: - ./.gitlab-ci-script.sh + after_script: + - curl -d "repo=github.com/oidc-mytoken/server" https://goreportcard.com/checks diff --git a/.goreleaser.yml b/.goreleaser.yml index b70a089b..0b296b0c 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -21,8 +21,8 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' - id: migratedb main: ./cmd/mytoken-server/mytoken-migratedb @@ -31,18 +31,17 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' archives: - - - replacements: - darwin: macOS - 386: 32-bit - amd64: 64-bit - format_overrides: - - goos: windows - format: zip + - replacements: + darwin: macOS + 386: 32-bit + amd64: 64-bit + format_overrides: + - goos: windows + format: zip nfpms: - id: server-pkg package_name: mytoken-server @@ -51,7 +50,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - server - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: Mytoken is a central web service with the goal to easily obtain OpenID Connect access tokens across devices. license: MIT @@ -81,7 +80,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - setup - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: A setup utility for the mytoken-server license: MIT @@ -103,7 +102,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - migratedb - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: A tool for migrating the database between versions license: MIT From 4f6e9144f3a4a812adc2608c47ba4eda3bdfd6e3 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 27 Oct 2022 08:31:01 +0200 Subject: [PATCH 19/55] fix misuse of unbuffered os.Signal channel as argument to signal.Notify --- cmd/mytoken-server/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/mytoken-server/main.go b/cmd/mytoken-server/main.go index 433dd230..67be8322 100644 --- a/cmd/mytoken-server/main.go +++ b/cmd/mytoken-server/main.go @@ -42,7 +42,7 @@ func main() { } func handleSignals() { - signals := make(chan os.Signal) + signals := make(chan os.Signal, 1) signal.Notify(signals, syscall.SIGHUP, syscall.SIGUSR1) go func() { for { From 2ebaa06865200a198ebf45d6da5a05f0f996cfd9 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 27 Oct 2022 08:40:29 +0200 Subject: [PATCH 20/55] add caching --- .gitlab-ci.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 18c107d5..31a94d4a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,6 +8,13 @@ stages: default: tags: - linux + cache: + paths: + - .cache + +before_script: + - mkdir -p .cache + - export GOPATH=${CI_PROJECT_DIR}/.cache test: stage: test @@ -58,7 +65,10 @@ prerelease: - linux variables: GIT_DEPTH: 0 - # DOCKER_REGISTRY: https://registry.hub.docker.com/v1/ + REPO_HOST: repo.data.kit.edu + REPO_USER: cicd + REPO_BASE: /var/www/prerel + REPO_SIGN_KEY: F35D41344946F23D5D77AFD1E89B3987AEB6032B script: - ./.gitlab-ci-script.sh after_script: From 371025f3a342494eec54040f8f5834f34e451bb4 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 27 Oct 2022 08:43:48 +0200 Subject: [PATCH 21/55] fix golint install --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 31a94d4a..1ecb8765 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ test_race: lint: stage: lint before_script: - - go install golang.org/x/lint/golint + - go install golang.org/x/lint/golint@latest script: - golint -set_exit_status ./... From 4ee27a1e6fa5ef8494eeacb390a7423bcba9ba19 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 27 Oct 2022 09:12:39 +0200 Subject: [PATCH 22/55] fix golint issues --- cmd/mytoken-server/main.go | 4 +- internal/config/config.go | 4 +- internal/db/cluster/cluster.go | 3 +- internal/db/db.go | 3 +- .../db/dbrepo/accesstokenrepo/accessToken.go | 4 +- .../authcodeinforepo/state/consentcode.go | 4 +- .../db/dbrepo/authcodeinforepo/state/state.go | 6 +-- internal/db/dbrepo/cryptstore/cryptstore.go | 4 +- .../encryptionkeyrepo/encryptionkeyRepo.go | 10 ++--- internal/db/dbrepo/mytokenrepo/mytoken.go | 10 ++--- .../mytokenrepo/mytokenrepohelper/helpers.go | 9 ++--- .../transfercoderepo/proxytoken.go | 14 +++---- internal/db/dbrepo/sshrepo/sshrepo.go | 4 +- internal/endpoints/consent/consent.go | 8 ++-- .../endpoints/redirect/redirectEndpoint.go | 8 ++-- internal/endpoints/settings/grants/ssh/ssh.go | 14 +++---- internal/endpoints/settings/settings.go | 5 ++- .../token/access/accessTokenEndpoint.go | 6 +-- .../token/mytoken/mytokenEndpoint.go | 4 +- .../token/mytoken/polling/pollingEndpoint.go | 4 +- .../token/mytoken/transferEndpoint.go | 4 +- internal/endpoints/tokeninfo/tokeninfo.go | 4 +- internal/oidc/authcode/authcode.go | 14 +++---- .../{oidcReqRes => oidcreqres}/request.go | 2 +- .../{oidcReqRes => oidcreqres}/response.go | 2 +- internal/oidc/pkce/pkce.go | 22 +++++------ internal/oidc/refresh/refresh.go | 38 +++++++++---------- internal/oidc/revoke/revoke.go | 12 +++--- .../server/{apiPath => apipath}/apiPath.go | 2 +- .../{httpStatus => httpstatus}/httpStatus.go | 2 +- internal/server/middlerwares.go | 4 +- internal/server/routes/routes.go | 17 +++++---- internal/server/server.go | 4 +- internal/server/ssh/sshServer.go | 4 +- internal/utils/auth/auther.go | 4 +- internal/utils/cookies/cookies.go | 6 +-- .../{ctxUtils => ctxutils}/authHeader.go | 2 +- .../utils/{ctxUtils => ctxutils}/grantType.go | 2 +- .../{ctxUtils => ctxutils}/networkData.go | 2 +- .../utils/{ctxUtils => ctxutils}/oidcFlow.go | 2 +- .../utils/{ctxUtils => ctxutils}/token.go | 2 +- internal/utils/fileio/filesystem.go | 16 ++++---- internal/utils/fileio/join.go | 1 + .../{hashUtils => hashutils}/hashUtils.go | 4 +- .../hashUtils_test.go | 2 +- internal/utils/logger/smartlogger.go | 8 ++-- internal/utils/templating/keys.go | 2 +- internal/utils/utils.go | 8 ++-- internal/utils/zipdownload/zipdownload.go | 4 +- .../{httpClient => httpclient}/httpClient.go | 2 +- shared/mytoken/mytokenHandler.go | 12 +++--- shared/mytoken/pkg/mtid/mtid.go | 4 +- shared/mytoken/restrictions/restriction.go | 18 ++++----- .../{cryptUtils => cryptutils}/cryptUtils.go | 2 +- .../issuerUtils.go | 7 ++-- .../issuerUtils_test.go | 2 +- 56 files changed, 186 insertions(+), 185 deletions(-) rename internal/oidc/{oidcReqRes => oidcreqres}/request.go (99%) rename internal/oidc/{oidcReqRes => oidcreqres}/response.go (96%) rename internal/server/{apiPath => apipath}/apiPath.go (88%) rename internal/server/{httpStatus => httpstatus}/httpStatus.go (83%) rename internal/utils/{ctxUtils => ctxutils}/authHeader.go (95%) rename internal/utils/{ctxUtils => ctxutils}/grantType.go (98%) rename internal/utils/{ctxUtils => ctxutils}/networkData.go (94%) rename internal/utils/{ctxUtils => ctxutils}/oidcFlow.go (98%) rename internal/utils/{ctxUtils => ctxutils}/token.go (98%) rename internal/utils/{hashUtils => hashutils}/hashUtils.go (97%) rename internal/utils/{hashUtils => hashutils}/hashUtils_test.go (98%) rename shared/{httpClient => httpclient}/httpClient.go (97%) rename shared/utils/{cryptUtils => cryptutils}/cryptUtils.go (99%) rename shared/utils/{issuerUtils => issuerutils}/issuerUtils.go (91%) rename shared/utils/{issuerUtils => issuerutils}/issuerUtils_test.go (99%) diff --git a/cmd/mytoken-server/main.go b/cmd/mytoken-server/main.go index 67be8322..aa7e438d 100644 --- a/cmd/mytoken-server/main.go +++ b/cmd/mytoken-server/main.go @@ -12,6 +12,7 @@ import ( "github.com/oidc-mytoken/server/internal/endpoints/settings" "github.com/oidc-mytoken/server/internal/model/version" "github.com/oidc-mytoken/server/internal/utils/cookies" + "github.com/oidc-mytoken/server/shared/httpclient" "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/db" @@ -21,7 +22,6 @@ import ( "github.com/oidc-mytoken/server/internal/server" "github.com/oidc-mytoken/server/internal/utils/geoip" loggerUtils "github.com/oidc-mytoken/server/internal/utils/logger" - "github.com/oidc-mytoken/server/shared/httpClient" ) func main() { @@ -33,7 +33,7 @@ func main() { authcode.Init() versionrepo.ConnectToVersion() jws.LoadKey() - httpClient.Init(config.Get().IssuerURL, fmt.Sprintf("mytoken-server %s", version.VERSION())) + httpclient.Init(config.Get().IssuerURL, fmt.Sprintf("mytoken-server %s", version.VERSION())) geoip.Init() settings.InitSettings() cookies.Init() diff --git a/internal/config/config.go b/internal/config/config.go index 1362d728..9c518bfc 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -14,12 +14,12 @@ import ( model2 "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/utils/errorfmt" + "github.com/oidc-mytoken/server/shared/utils/issuerutils" "github.com/oidc-mytoken/server/pkg/oauth2x" "github.com/oidc-mytoken/server/shared/context" "github.com/oidc-mytoken/server/shared/utils" "github.com/oidc-mytoken/server/shared/utils/fileutil" - "github.com/oidc-mytoken/server/shared/utils/issuerUtils" ) var defaultConfig = Config{ @@ -417,7 +417,7 @@ func validate() error { if len(p.Scopes) <= 0 { return errors.Errorf("invalid config: provider.scopes not set (Index %d)", i) } - iss0, iss1 := issuerUtils.GetIssuerWithAndWithoutSlash(p.Issuer) + iss0, iss1 := issuerutils.GetIssuerWithAndWithoutSlash(p.Issuer) conf.ProviderByIssuer[iss0] = p conf.ProviderByIssuer[iss1] = p if p.AudienceRequestParameter == "" { diff --git a/internal/db/cluster/cluster.go b/internal/db/cluster/cluster.go index 3570998d..dcee805c 100644 --- a/internal/db/cluster/cluster.go +++ b/internal/db/cluster/cluster.go @@ -225,7 +225,6 @@ func (c *Cluster) next(rlog log.Ext1FieldLogger) *node { func (c *Cluster) RunWithinTransaction(rlog log.Ext1FieldLogger, tx *sqlx.Tx, fn func(*sqlx.Tx) error) error { if tx == nil { return c.Transact(rlog, fn) - } else { - return fn(tx) } + return fn(tx) } diff --git a/internal/db/db.go b/internal/db/db.go index 3f925649..7dc34d2d 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -100,9 +100,8 @@ type BitBool bool func (b BitBool) Value() (driver.Value, error) { if b { return []byte{1}, nil - } else { - return []byte{0}, nil } + return []byte{0}, nil } // Scan implements the sql.Scanner interface, diff --git a/internal/db/dbrepo/accesstokenrepo/accessToken.go b/internal/db/dbrepo/accesstokenrepo/accessToken.go index 997c4ac5..9efb5bb0 100644 --- a/internal/db/dbrepo/accesstokenrepo/accessToken.go +++ b/internal/db/dbrepo/accesstokenrepo/accessToken.go @@ -9,7 +9,7 @@ import ( "github.com/oidc-mytoken/server/internal/model" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // AccessToken holds database information about an access token @@ -35,7 +35,7 @@ func (t *AccessToken) toDBObject() (*accessToken, error) { if err != nil { return nil, err } - token, err := cryptUtils.AES256Encrypt(t.Token, stJWT) + token, err := cryptutils.AES256Encrypt(t.Token, stJWT) if err != nil { return nil, err } diff --git a/internal/db/dbrepo/authcodeinforepo/state/consentcode.go b/internal/db/dbrepo/authcodeinforepo/state/consentcode.go index 8b9894c7..9b0b0455 100644 --- a/internal/db/dbrepo/authcodeinforepo/state/consentcode.go +++ b/internal/db/dbrepo/authcodeinforepo/state/consentcode.go @@ -1,7 +1,7 @@ package state import ( - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" "github.com/oidc-mytoken/server/shared/utils" ) @@ -32,7 +32,7 @@ func ConsentCodeFromStr(code string) *ConsentCode { // GetState returns the state linked to a ConsentCode func (c *ConsentCode) GetState() string { if c.state == "" { - c.state = hashUtils.HMACBasedHash([]byte(c.code))[:stateLen] + c.state = hashutils.HMACBasedHash([]byte(c.code))[:stateLen] } return c.state } diff --git a/internal/db/dbrepo/authcodeinforepo/state/state.go b/internal/db/dbrepo/authcodeinforepo/state/state.go index d70d2892..45c5e211 100644 --- a/internal/db/dbrepo/authcodeinforepo/state/state.go +++ b/internal/db/dbrepo/authcodeinforepo/state/state.go @@ -9,7 +9,7 @@ import ( "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/db" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" ) const stateLen = 16 @@ -38,7 +38,7 @@ func NewState(state string) *State { // Hash returns the hash for this State func (s *State) Hash() string { if s.hash == "" { - s.hash = hashUtils.SHA3_512Str([]byte(s.state)) + s.hash = hashutils.SHA3_512Str([]byte(s.state)) } return s.hash } @@ -46,7 +46,7 @@ func (s *State) Hash() string { // PollingCode returns the polling code for this State func (s *State) PollingCode(rlog log.Ext1FieldLogger) string { if s.pollingCode == "" { - s.pollingCode = hashUtils.HMACBasedHash([]byte(s.state))[:config.Get().Features.Polling.Len] + s.pollingCode = hashutils.HMACBasedHash([]byte(s.state))[:config.Get().Features.Polling.Len] rlog.WithField("state", s.state).WithField( "polling_code", s.pollingCode, ).Debug("Created polling_code for state") diff --git a/internal/db/dbrepo/cryptstore/cryptstore.go b/internal/db/dbrepo/cryptstore/cryptstore.go index cf5ee524..6a3a6616 100644 --- a/internal/db/dbrepo/cryptstore/cryptstore.go +++ b/internal/db/dbrepo/cryptstore/cryptstore.go @@ -8,7 +8,7 @@ import ( "github.com/oidc-mytoken/server/internal/db" "github.com/oidc-mytoken/server/internal/db/dbrepo/encryptionkeyrepo" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // DeleteCrypted deletes an entry from the CryptStore @@ -29,7 +29,7 @@ func UpdateRefreshToken(rlog log.Ext1FieldLogger, tx *sqlx.Tx, tokenID mtid.MTID if err != nil { return err } - updatedRT, err := cryptUtils.AESEncrypt(newRT, key) + updatedRT, err := cryptutils.AESEncrypt(newRT, key) if err != nil { return err } diff --git a/internal/db/dbrepo/encryptionkeyrepo/encryptionkeyRepo.go b/internal/db/dbrepo/encryptionkeyrepo/encryptionkeyRepo.go index 77b76a07..f963c9cd 100644 --- a/internal/db/dbrepo/encryptionkeyrepo/encryptionkeyRepo.go +++ b/internal/db/dbrepo/encryptionkeyrepo/encryptionkeyRepo.go @@ -9,7 +9,7 @@ import ( "github.com/oidc-mytoken/server/internal/db" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // ReencryptEncryptionKey re-encrypts the encryption key for a mytoken. This is needed when the mytoken changes, e.g. on @@ -25,11 +25,11 @@ func ReencryptEncryptionKey(rlog log.Ext1FieldLogger, tx *sqlx.Tx, tokenID mtid. if err = errors.WithStack(tx.Get(&encryptedKey, `CALL EncryptionKeys_Get(?)`, keyID)); err != nil { return err } - key, err := cryptUtils.AES256Decrypt(encryptedKey, oldJWT) + key, err := cryptutils.AES256Decrypt(encryptedKey, oldJWT) if err != nil { return err } - updatedKey, err := cryptUtils.AES256Encrypt(key, newJWT) + updatedKey, err := cryptutils.AES256Encrypt(key, newJWT) if err != nil { return err } @@ -78,7 +78,7 @@ type EncryptionKey string // Decrypt returns the decrypted encryption key func (k EncryptionKey) Decrypt(jwt string) ([]byte, error) { - decryptedKey, err := cryptUtils.AES256Decrypt(string(k), jwt) + decryptedKey, err := cryptutils.AES256Decrypt(string(k), jwt) if err != nil { return nil, err } @@ -100,7 +100,7 @@ func (res RTCryptKeyDBRes) Decrypt(jwt string) (string, error) { if err != nil { return "", err } - return cryptUtils.AESDecrypt(res.RT, key) + return cryptutils.AESDecrypt(res.RT, key) } // getEncryptionKeyID returns the id of the encryption key used for encrypting the RT linked to this mytoken diff --git a/internal/db/dbrepo/mytokenrepo/mytoken.go b/internal/db/dbrepo/mytokenrepo/mytoken.go index 7a0f91f0..0ae7ca58 100644 --- a/internal/db/dbrepo/mytokenrepo/mytoken.go +++ b/internal/db/dbrepo/mytokenrepo/mytoken.go @@ -15,7 +15,7 @@ import ( event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" "github.com/oidc-mytoken/server/shared/utils/unixtime" ) @@ -40,8 +40,8 @@ type MytokenEntry struct { // InitRefreshToken links a refresh token to this MytokenEntry func (mte *MytokenEntry) InitRefreshToken(rt string) error { mte.refreshToken = rt - mte.encryptionKey = cryptUtils.RandomBytes(32) - tmp, err := cryptUtils.AESEncrypt(mte.refreshToken, mte.encryptionKey) + mte.encryptionKey = cryptutils.RandomBytes(32) + tmp, err := cryptutils.AESEncrypt(mte.refreshToken, mte.encryptionKey) if err != nil { return err } @@ -50,7 +50,7 @@ func (mte *MytokenEntry) InitRefreshToken(rt string) error { if err != nil { return err } - tmp, err = cryptUtils.AES256Encrypt(base64.StdEncoding.EncodeToString(mte.encryptionKey), jwt) + tmp, err = cryptutils.AES256Encrypt(base64.StdEncoding.EncodeToString(mte.encryptionKey), jwt) if err != nil { return err } @@ -65,7 +65,7 @@ func (mte *MytokenEntry) SetRefreshToken(rtID uint64, key []byte) error { if err != nil { return err } - tmp, err := cryptUtils.AES256Encrypt(base64.StdEncoding.EncodeToString(key), jwt) + tmp, err := cryptutils.AES256Encrypt(base64.StdEncoding.EncodeToString(key), jwt) if err != nil { return err } diff --git a/internal/db/dbrepo/mytokenrepo/mytokenrepohelper/helpers.go b/internal/db/dbrepo/mytokenrepo/mytokenrepohelper/helpers.go index 14ecae60..fa16bee4 100644 --- a/internal/db/dbrepo/mytokenrepo/mytokenrepohelper/helpers.go +++ b/internal/db/dbrepo/mytokenrepo/mytokenrepohelper/helpers.go @@ -10,7 +10,7 @@ import ( "github.com/oidc-mytoken/server/internal/db" "github.com/oidc-mytoken/server/internal/db/dbrepo/encryptionkeyrepo" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" ) @@ -129,9 +129,8 @@ func revokeMT(rlog log.Ext1FieldLogger, tx *sqlx.Tx, id interface{}) error { func RevokeMT(rlog log.Ext1FieldLogger, tx *sqlx.Tx, id interface{}, recursive bool) error { if recursive { return recursiveRevokeMT(rlog, tx, id) - } else { - return revokeMT(rlog, tx, id) } + return revokeMT(rlog, tx, id) } // RevocationIDHasParent checks if the token for a revocation id is a child of the (potential) parent mytoken @@ -197,7 +196,7 @@ func IncreaseTokenUsageAT(rlog log.Ext1FieldLogger, tx *sqlx.Tx, myID mtid.MTID, rlog, tx, func(tx *sqlx.Tx) error { _, err := tx.Exec( `CALL TokenUsages_IncrAT(?,?,?)`, - myID, jsonRestriction, hashUtils.SHA512Str(jsonRestriction), + myID, jsonRestriction, hashutils.SHA512Str(jsonRestriction), ) return errors.WithStack(err) }, @@ -210,7 +209,7 @@ func IncreaseTokenUsageOther(rlog log.Ext1FieldLogger, tx *sqlx.Tx, myID mtid.MT rlog, tx, func(tx *sqlx.Tx) error { _, err := tx.Exec( `CALL TokenUsages_IncrOther(?,?,?)`, - myID, jsonRestriction, hashUtils.SHA512Str(jsonRestriction), + myID, jsonRestriction, hashutils.SHA512Str(jsonRestriction), ) return errors.WithStack(err) }, diff --git a/internal/db/dbrepo/mytokenrepo/transfercoderepo/proxytoken.go b/internal/db/dbrepo/mytokenrepo/transfercoderepo/proxytoken.go index 2dac7918..b0a6f936 100644 --- a/internal/db/dbrepo/mytokenrepo/transfercoderepo/proxytoken.go +++ b/internal/db/dbrepo/mytokenrepo/transfercoderepo/proxytoken.go @@ -8,10 +8,10 @@ import ( log "github.com/sirupsen/logrus" "github.com/oidc-mytoken/server/internal/db" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" "github.com/oidc-mytoken/server/shared/utils" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // proxyToken holds information for proxy tokens, i.e. tokens that proxy another token, e.g. a short token @@ -31,7 +31,7 @@ func newProxyToken(size int) *proxyToken { } func createProxyToken(token string) *proxyToken { - id := hashUtils.SHA512Str([]byte(token)) + id := hashutils.SHA512Str([]byte(token)) return &proxyToken{ id: id, token: token, @@ -42,7 +42,7 @@ func createProxyToken(token string) *proxyToken { func parseProxyToken(token string) *proxyToken { var id string if token != "" { - id = hashUtils.SHA512Str([]byte(token)) + id = hashutils.SHA512Str([]byte(token)) } return &proxyToken{ id: id, @@ -62,7 +62,7 @@ func (pt proxyToken) Token() string { // ID returns the id of this token func (pt *proxyToken) ID() string { if pt.id == "" { - pt.id = hashUtils.SHA512Str([]byte(pt.token)) + pt.id = hashutils.SHA512Str([]byte(pt.token)) } return pt.id } @@ -71,7 +71,7 @@ func (pt *proxyToken) ID() string { func (pt *proxyToken) SetJWT(jwt string, mID mtid.MTID) (err error) { pt.mtID = mID pt.decryptedJWT = jwt - pt.encryptedJWT, err = cryptUtils.AES256Encrypt(jwt, pt.token) + pt.encryptedJWT, err = cryptutils.AES256Encrypt(jwt, pt.token) return } @@ -108,7 +108,7 @@ func (pt *proxyToken) JWT(rlog log.Ext1FieldLogger, tx *sqlx.Tx) (jwt string, va if pt.encryptedJWT == "" { return } - jwt, err = cryptUtils.AES256Decrypt(pt.encryptedJWT, pt.token) + jwt, err = cryptutils.AES256Decrypt(pt.encryptedJWT, pt.token) pt.decryptedJWT = jwt return } diff --git a/internal/db/dbrepo/sshrepo/sshrepo.go b/internal/db/dbrepo/sshrepo/sshrepo.go index 7b7e0010..524994b2 100644 --- a/internal/db/dbrepo/sshrepo/sshrepo.go +++ b/internal/db/dbrepo/sshrepo/sshrepo.go @@ -13,7 +13,7 @@ import ( mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" "github.com/oidc-mytoken/server/shared/utils" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // GetSSHInfo returns the SSHInfo stored in the database for the passed key and user hashes. @@ -65,7 +65,7 @@ type SSHInfo struct { // Decrypt decrypts the encrypted mytoken linked to this ssh key with the passed password func (i SSHInfo) Decrypt(password string) (*mytoken.Mytoken, error) { - decryptedMT, err := cryptUtils.AES256Decrypt(i.EncryptedMT, password) + decryptedMT, err := cryptutils.AES256Decrypt(i.EncryptedMT, password) if err != nil { return nil, err } diff --git a/internal/endpoints/consent/consent.go b/internal/endpoints/consent/consent.go index fe6ad91c..92e328d8 100644 --- a/internal/endpoints/consent/consent.go +++ b/internal/endpoints/consent/consent.go @@ -15,7 +15,7 @@ import ( "github.com/oidc-mytoken/server/internal/db" pkg2 "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" - "github.com/oidc-mytoken/server/internal/server/httpStatus" + "github.com/oidc-mytoken/server/internal/server/httpstatus" "github.com/oidc-mytoken/server/internal/utils/auth" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" @@ -58,7 +58,7 @@ func handleConsent(ctx *fiber.Ctx, info *pkg2.OIDCFlowRequest, includeConsentCal if iss[len(iss)-1] == '/' { iss = iss[:len(iss)-1] } - binding[templating.MustacheKeyInstanceUrl] = iss + binding[templating.MustacheKeyInstanceURL] = iss } return ctx.Render("sites/consent", binding, "layouts/main") } @@ -144,7 +144,7 @@ func handleConsentDecline(ctx *fiber.Ctx, authInfo *authcodeinforepo.AuthFlowInf } } return model.Response{ - Status: httpStatus.StatusOKForward, + Status: httpstatus.StatusOKForward, Response: map[string]string{ "url": url, }, @@ -188,7 +188,7 @@ func handleConsentAccept( return model.ErrorToInternalServerErrorResponse(err) } return &model.Response{ - Status: httpStatus.StatusOKForward, + Status: httpstatus.StatusOKForward, Response: map[string]string{ "authorization_uri": authURI, }, diff --git a/internal/endpoints/redirect/redirectEndpoint.go b/internal/endpoints/redirect/redirectEndpoint.go index 47e8c64a..5fe8b4f4 100644 --- a/internal/endpoints/redirect/redirectEndpoint.go +++ b/internal/endpoints/redirect/redirectEndpoint.go @@ -13,8 +13,8 @@ import ( "github.com/oidc-mytoken/server/internal/db/dbrepo/authcodeinforepo/state" "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/transfercoderepo" "github.com/oidc-mytoken/server/internal/oidc/authcode" - "github.com/oidc-mytoken/server/internal/server/httpStatus" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/server/httpstatus" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" pkgModel "github.com/oidc-mytoken/server/shared/model" @@ -40,7 +40,7 @@ func HandleOIDCRedirect(ctx *fiber.Ctx) error { } } oidcErrorDescription := ctx.Query("error_description") - return ctx.Status(httpStatus.StatusOIDPError).Render( + return ctx.Status(httpstatus.StatusOIDPError).Render( "sites/error", map[string]interface{}{ "empty-navbar": true, "error-heading": "OIDC error", @@ -49,7 +49,7 @@ func HandleOIDCRedirect(ctx *fiber.Ctx) error { ) } code := ctx.Query("code") - res := authcode.CodeExchange(rlog, oState, code, *ctxUtils.ClientMetaData(ctx)) + res := authcode.CodeExchange(rlog, oState, code, *ctxutils.ClientMetaData(ctx)) if fasthttp.StatusCodeIsRedirect(res.Status) { return res.Send(ctx) diff --git a/internal/endpoints/settings/grants/ssh/ssh.go b/internal/endpoints/settings/grants/ssh/ssh.go index 1db8620f..bd844cb2 100644 --- a/internal/endpoints/settings/grants/ssh/ssh.go +++ b/internal/endpoints/settings/grants/ssh/ssh.go @@ -21,16 +21,16 @@ import ( serverModel "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/oidc/authcode" "github.com/oidc-mytoken/server/internal/server/ssh" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" "github.com/oidc-mytoken/server/internal/utils/logger" "github.com/oidc-mytoken/server/shared/model" event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/universalmytoken" "github.com/oidc-mytoken/server/shared/utils" - "github.com/oidc-mytoken/server/shared/utils/cryptUtils" + "github.com/oidc-mytoken/server/shared/utils/cryptutils" ) // HandleGetSSHInfo handles requests to return information about a user's ssh keys @@ -102,7 +102,7 @@ func HandleDeleteSSHKey(ctx *fiber.Ctx) error { // HandlePost handles POST requests to the ssh grant endpoint, this includes the initial request to add an ssh public // key as well as the following polling requests. func HandlePost(ctx *fiber.Ctx) error { - grantType, err := ctxUtils.GetGrantType(ctx) + grantType, err := ctxutils.GetGrantType(ctx) if err != nil { return serverModel.ErrorToBadRequestErrorResponse(err).Send(ctx) } @@ -249,14 +249,14 @@ func handlePollingCode(ctx *fiber.Ctx) error { if err := json.Unmarshal(ctx.Body(), &req); err != nil { return serverModel.ErrorToBadRequestErrorResponse(err).Send(ctx) } - clientMetaData := ctxUtils.ClientMetaData(ctx) + clientMetaData := ctxutils.ClientMetaData(ctx) mt, token, status, errRes := polling.CheckPollingCodeReq(rlog, req, *clientMetaData, true) if errRes != nil { return errRes.Send(ctx) } user := utils.RandASCIIString(16) - userHash := hashUtils.SHA3_512Str([]byte(user)) - encryptedMT, err := cryptUtils.AES256Encrypt(token, user) + userHash := hashutils.SHA3_512Str([]byte(user)) + encryptedMT, err := cryptutils.AES256Encrypt(token, user) if err != nil { rlog.Errorf("%s", errorfmt.Full(err)) return serverModel.ErrorToInternalServerErrorResponse(err).Send(ctx) diff --git a/internal/endpoints/settings/settings.go b/internal/endpoints/settings/settings.go index d796b50b..e88f9875 100644 --- a/internal/endpoints/settings/settings.go +++ b/internal/endpoints/settings/settings.go @@ -14,7 +14,7 @@ import ( "github.com/oidc-mytoken/server/internal/server/routes" "github.com/oidc-mytoken/server/internal/utils/auth" "github.com/oidc-mytoken/server/internal/utils/cookies" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" eventService "github.com/oidc-mytoken/server/shared/mytoken/event" @@ -37,6 +37,7 @@ var settingsMetadata = api.SettingsMetaData{ GrantTypeEndpoint: "grants", } +// HandleSettings handles Metadata requests to the settings endpoint func HandleSettings(ctx *fiber.Ctx) error { res := serverModel.Response{ Status: fiber.StatusOK, @@ -77,7 +78,7 @@ func HandleSettingsHelper( if tokenGoneAfterCallback { return } - clientMetaData := ctxUtils.ClientMetaData(ctx) + clientMetaData := ctxutils.ClientMetaData(ctx) if logEvent != nil { if err = eventService.LogEvent( rlog, tx, eventService.MTEvent{ diff --git a/internal/endpoints/token/access/accessTokenEndpoint.go b/internal/endpoints/token/access/accessTokenEndpoint.go index d9406aed..6bf84fb5 100644 --- a/internal/endpoints/token/access/accessTokenEndpoint.go +++ b/internal/endpoints/token/access/accessTokenEndpoint.go @@ -18,7 +18,7 @@ import ( "github.com/oidc-mytoken/server/internal/oidc/refresh" "github.com/oidc-mytoken/server/internal/utils/auth" "github.com/oidc-mytoken/server/internal/utils/cookies" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" "github.com/oidc-mytoken/server/shared/model" @@ -65,7 +65,7 @@ func HandleAccessTokenEndpoint(ctx *fiber.Ctx) error { return errRes.Send(ctx) } - return HandleAccessTokenRefresh(rlog, mt, req, *ctxUtils.ClientMetaData(ctx), provider, usedRestriction).Send(ctx) + return HandleAccessTokenRefresh(rlog, mt, req, *ctxutils.ClientMetaData(ctx), provider, usedRestriction).Send(ctx) } func parseScopesAndAudienceToUse( @@ -110,7 +110,7 @@ func HandleAccessTokenRefresh( } scopes, auds := parseScopesAndAudienceToUse(req.Scope, req.Audience, usedRestriction, provider.Scopes) - oidcRes, oidcErrRes, err := refresh.RefreshFlowAndUpdateDB(rlog, provider, mt.ID, req.Mytoken.JWT, rt, scopes, auds) + oidcRes, oidcErrRes, err := refresh.DoFlowAndUpdateDB(rlog, provider, mt.ID, req.Mytoken.JWT, rt, scopes, auds) if err != nil { rlog.Errorf("%s", errorfmt.Full(err)) return serverModel.ErrorToInternalServerErrorResponse(err) diff --git a/internal/endpoints/token/mytoken/mytokenEndpoint.go b/internal/endpoints/token/mytoken/mytokenEndpoint.go index 2e6496a2..05b6a01c 100644 --- a/internal/endpoints/token/mytoken/mytokenEndpoint.go +++ b/internal/endpoints/token/mytoken/mytokenEndpoint.go @@ -11,7 +11,7 @@ import ( "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/polling" serverModel "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/oidc/authcode" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/logger" "github.com/oidc-mytoken/server/shared/model" "github.com/oidc-mytoken/server/shared/mytoken" @@ -25,7 +25,7 @@ var defaultCapabilities = api.Capabilities{ // HandleMytokenEndpoint handles requests on the mytoken endpoint func HandleMytokenEndpoint(ctx *fiber.Ctx) error { rlog := logger.GetRequestLogger(ctx) - grantType, err := ctxUtils.GetGrantType(ctx) + grantType, err := ctxutils.GetGrantType(ctx) if err != nil { return serverModel.ErrorToBadRequestErrorResponse(err).Send(ctx) } diff --git a/internal/endpoints/token/mytoken/polling/pollingEndpoint.go b/internal/endpoints/token/mytoken/polling/pollingEndpoint.go index d51c07ff..8622d0d3 100644 --- a/internal/endpoints/token/mytoken/polling/pollingEndpoint.go +++ b/internal/endpoints/token/mytoken/polling/pollingEndpoint.go @@ -10,7 +10,7 @@ import ( "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/transfercoderepo" response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" @@ -23,7 +23,7 @@ func HandlePollingCode(ctx *fiber.Ctx) error { if err := json.Unmarshal(ctx.Body(), &req); err != nil { return model.ErrorToBadRequestErrorResponse(err).Send(ctx) } - clientMetaData := ctxUtils.ClientMetaData(ctx) + clientMetaData := ctxutils.ClientMetaData(ctx) mt, token, pollingCodeStatus, errRes := CheckPollingCodeReq(rlog, req, *clientMetaData, false) if errRes != nil { return errRes.Send(ctx) diff --git a/internal/endpoints/token/mytoken/transferEndpoint.go b/internal/endpoints/token/mytoken/transferEndpoint.go index abd9cdef..5da6de2d 100644 --- a/internal/endpoints/token/mytoken/transferEndpoint.go +++ b/internal/endpoints/token/mytoken/transferEndpoint.go @@ -9,7 +9,7 @@ import ( "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/utils/auth" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" pkgModel "github.com/oidc-mytoken/server/shared/model" @@ -32,7 +32,7 @@ func HandleCreateTransferCodeForExistingMytoken(ctx *fiber.Ctx) error { } transferCode, expiresIn, err := mytoken.CreateTransferCode( - rlog, mt.ID, req.Mytoken.JWT, false, req.Mytoken.OriginalTokenType, *ctxUtils.ClientMetaData(ctx), + rlog, mt.ID, req.Mytoken.JWT, false, req.Mytoken.OriginalTokenType, *ctxutils.ClientMetaData(ctx), ) if err != nil { rlog.Errorf("%s", errorfmt.Full(err)) diff --git a/internal/endpoints/tokeninfo/tokeninfo.go b/internal/endpoints/tokeninfo/tokeninfo.go index f313fc49..40343fee 100644 --- a/internal/endpoints/tokeninfo/tokeninfo.go +++ b/internal/endpoints/tokeninfo/tokeninfo.go @@ -11,7 +11,7 @@ import ( "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/utils/auth" "github.com/oidc-mytoken/server/internal/utils/cookies" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/logger" model2 "github.com/oidc-mytoken/server/shared/model" ) @@ -27,7 +27,7 @@ func HandleTokenInfo(ctx *fiber.Ctx) error { if errRes != nil { return errRes.Send(ctx) } - clientMetadata := ctxUtils.ClientMetaData(ctx) + clientMetadata := ctxutils.ClientMetaData(ctx) switch req.Action { case model2.TokeninfoActionIntrospect: return HandleTokenInfoIntrospect(rlog, mt, req.Mytoken.OriginalTokenType, clientMetadata).Send(ctx) diff --git a/internal/oidc/authcode/authcode.go b/internal/oidc/authcode/authcode.go index 08ec19ef..300d3a77 100644 --- a/internal/oidc/authcode/authcode.go +++ b/internal/oidc/authcode/authcode.go @@ -24,7 +24,7 @@ import ( "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/oidc/issuer" "github.com/oidc-mytoken/server/internal/oidc/pkce" - "github.com/oidc-mytoken/server/internal/server/httpStatus" + "github.com/oidc-mytoken/server/internal/server/httpstatus" "github.com/oidc-mytoken/server/internal/server/routes" "github.com/oidc-mytoken/server/internal/utils/cookies" "github.com/oidc-mytoken/server/internal/utils/errorfmt" @@ -34,7 +34,7 @@ import ( mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/restrictions" "github.com/oidc-mytoken/server/shared/utils" - "github.com/oidc-mytoken/server/shared/utils/issuerUtils" + "github.com/oidc-mytoken/server/shared/utils/issuerutils" "github.com/oidc-mytoken/server/shared/utils/jwtutils" "github.com/oidc-mytoken/server/shared/utils/ternary" "github.com/oidc-mytoken/server/shared/utils/unixtime" @@ -81,7 +81,7 @@ func GetAuthorizationURL( oauth2.SetAuthURLParam("code_challenge", pkceChallenge), oauth2.SetAuthURLParam("code_challenge_method", pkce.TransformationS256.String()), } - if issuerUtils.CompareIssuerURLs(provider.Issuer, issuer.GOOGLE) { + if issuerutils.CompareIssuerURLs(provider.Issuer, issuer.GOOGLE) { additionalParams = append(additionalParams, oauth2.AccessTypeOffline) } else if !utils.StringInSlice(oidc.ScopeOfflineAccess, oauth2Config.Scopes) { oauth2Config.Scopes = append(oauth2Config.Scopes, oidc.ScopeOfflineAccess) @@ -101,9 +101,9 @@ func GetAuthorizationURL( return oauth2Config.AuthCodeURL(oState.State(), additionalParams...), nil } -func trustedRedirectURI(redirectUri string) bool { +func trustedRedirectURI(redirectURI string) bool { for _, r := range config.Get().Features.OIDCFlows.AuthCode.Web.TrustedRedirectsRegex { - if r.MatchString(redirectUri) { + if r.MatchString(redirectURI) { return true } } @@ -125,7 +125,7 @@ func StartAuthCodeFlow(ctx *fiber.Ctx, oidcReq *response.OIDCFlowRequest) *model }, } } - req.Restrictions.ReplaceThisIp(ctx.IP()) + req.Restrictions.ReplaceThisIP(ctx.IP()) req.Restrictions.ClearUnsupportedKeys() provider, ok := config.Get().ProviderByIssuer[req.Issuer] if !ok { @@ -176,7 +176,7 @@ func StartAuthCodeFlow(ctx *fiber.Ctx, oidcReq *response.OIDCFlowRequest) *model return model.ErrorToInternalServerErrorResponse(err) } return &model.Response{ - Status: httpStatus.StatusOKForward, + Status: httpstatus.StatusOKForward, Response: map[string]string{ "authorization_uri": authURI, }, diff --git a/internal/oidc/oidcReqRes/request.go b/internal/oidc/oidcreqres/request.go similarity index 99% rename from internal/oidc/oidcReqRes/request.go rename to internal/oidc/oidcreqres/request.go index 109d3bad..ecb09dc9 100644 --- a/internal/oidc/oidcReqRes/request.go +++ b/internal/oidc/oidcreqres/request.go @@ -1,4 +1,4 @@ -package oidcReqRes +package oidcreqres import ( "encoding/json" diff --git a/internal/oidc/oidcReqRes/response.go b/internal/oidc/oidcreqres/response.go similarity index 96% rename from internal/oidc/oidcReqRes/response.go rename to internal/oidc/oidcreqres/response.go index 0e547409..a433ca2a 100644 --- a/internal/oidc/oidcReqRes/response.go +++ b/internal/oidc/oidcreqres/response.go @@ -1,4 +1,4 @@ -package oidcReqRes +package oidcreqres // OIDCErrorResponse is the error response of an oidc provider type OIDCErrorResponse struct { diff --git a/internal/oidc/pkce/pkce.go b/internal/oidc/pkce/pkce.go index d2363eb5..19252517 100644 --- a/internal/oidc/pkce/pkce.go +++ b/internal/oidc/pkce/pkce.go @@ -11,31 +11,31 @@ import ( type PKCE struct { verifier string challenge string - method PKCEMethod + method Method } -// PKCEMethod is a type for the code challenge methods -type PKCEMethod string +// Method is a type for the code challenge methods +type Method string -// Defines for the possible PKCEMethod +// Defines for the possible Method const ( - TransformationPlain = PKCEMethod("plain") - TransformationS256 = PKCEMethod("S256") + TransformationPlain = Method("plain") + TransformationS256 = Method("S256") ) -func (m PKCEMethod) String() string { +func (m Method) String() string { return string(m) } -// NewPKCE creates a new PKCE for the passed verifier and PKCEMethod -func NewPKCE(verifier string, method PKCEMethod) *PKCE { +// NewPKCE creates a new PKCE for the passed verifier and Method +func NewPKCE(verifier string, method Method) *PKCE { return &PKCE{ verifier: verifier, method: method, } } -// NewS256PKCE creates a new PKCE for the passed verifier and the PKCEMethod TransformationS256 +// NewS256PKCE creates a new PKCE for the passed verifier and the Method TransformationS256 func NewS256PKCE(verifier string) *PKCE { return NewPKCE(verifier, TransformationS256) } @@ -45,7 +45,7 @@ func (pkce PKCE) Verifier() string { return pkce.verifier } -// Challenge returns the code_challenge according to the defined PKCEMethod +// Challenge returns the code_challenge according to the defined Method func (pkce *PKCE) Challenge() (string, error) { var err error if pkce.challenge == "" { diff --git a/internal/oidc/refresh/refresh.go b/internal/oidc/refresh/refresh.go index c2ba276a..dba6773b 100644 --- a/internal/oidc/refresh/refresh.go +++ b/internal/oidc/refresh/refresh.go @@ -6,44 +6,44 @@ import ( "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/db/dbrepo/cryptstore" - "github.com/oidc-mytoken/server/internal/oidc/oidcReqRes" - "github.com/oidc-mytoken/server/shared/httpClient" + "github.com/oidc-mytoken/server/internal/oidc/oidcreqres" + "github.com/oidc-mytoken/server/shared/httpclient" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" ) // UpdateChangedRT is a function that should update a refresh token, it takes the old value as well as the new one type UpdateChangedRT func(rlog log.Ext1FieldLogger, tokenID mtid.MTID, newRT, mytoken string) error -// Refresh uses an refresh token to obtain a new access token; if the refresh token changes, this is ignored -func Refresh( +// DoFlowWithoutUpdate uses a refresh token to obtain a new access token; if the refresh token changes, this is ignored +func DoFlowWithoutUpdate( rlog log.Ext1FieldLogger, provider *config.ProviderConf, tokenID mtid.MTID, mytoken, rt, scopes, audiences string, -) (*oidcReqRes.OIDCTokenResponse, *oidcReqRes.OIDCErrorResponse, error) { - return RefreshFlowAndUpdate(rlog, provider, tokenID, mytoken, rt, scopes, audiences, nil) +) (*oidcreqres.OIDCTokenResponse, *oidcreqres.OIDCErrorResponse, error) { + return DoFlowAndUpdate(rlog, provider, tokenID, mytoken, rt, scopes, audiences, nil) } -// RefreshFlowAndUpdate uses an refresh token to obtain a new access token; if the refresh token changes, the +// DoFlowAndUpdate uses a refresh token to obtain a new access token; if the refresh token changes, the // UpdateChangedRT function is used to update the refresh token -func RefreshFlowAndUpdate( +func DoFlowAndUpdate( rlog log.Ext1FieldLogger, provider *config.ProviderConf, tokenID mtid.MTID, mytoken, rt, scopes, audiences string, updateFnc UpdateChangedRT, -) (*oidcReqRes.OIDCTokenResponse, *oidcReqRes.OIDCErrorResponse, error) { - req := oidcReqRes.NewRefreshRequest(rt, provider) +) (*oidcreqres.OIDCTokenResponse, *oidcreqres.OIDCErrorResponse, error) { + req := oidcreqres.NewRefreshRequest(rt, provider) req.Scopes = scopes req.Audiences = audiences - httpRes, err := httpClient.Do().R(). + httpRes, err := httpclient.Do().R(). SetBasicAuth(provider.ClientID, provider.ClientSecret). SetFormData(req.ToFormData()). - SetResult(&oidcReqRes.OIDCTokenResponse{}). - SetError(&oidcReqRes.OIDCErrorResponse{}). + SetResult(&oidcreqres.OIDCTokenResponse{}). + SetError(&oidcreqres.OIDCErrorResponse{}). Post(provider.Endpoints.Token) if err != nil { return nil, nil, errors.WithStack(err) } - if errRes, ok := httpRes.Error().(*oidcReqRes.OIDCErrorResponse); ok && errRes != nil && errRes.Error != "" { + if errRes, ok := httpRes.Error().(*oidcreqres.OIDCErrorResponse); ok && errRes != nil && errRes.Error != "" { errRes.Status = httpRes.RawResponse.StatusCode return nil, errRes, nil } - res, ok := httpRes.Result().(*oidcReqRes.OIDCTokenResponse) + res, ok := httpRes.Result().(*oidcreqres.OIDCTokenResponse) if !ok { return nil, nil, errors.New("could not unmarshal oidc response") } @@ -55,12 +55,12 @@ func RefreshFlowAndUpdate( return res, nil, nil } -// RefreshFlowAndUpdateDB uses an refresh token to obtain a new access token; if the refresh token changes, it is +// DoFlowAndUpdateDB uses a refresh token to obtain a new access token; if the refresh token changes, it is // updated in the database -func RefreshFlowAndUpdateDB( +func DoFlowAndUpdateDB( rlog log.Ext1FieldLogger, provider *config.ProviderConf, tokenID mtid.MTID, mytoken, rt, scopes, audiences string, -) (*oidcReqRes.OIDCTokenResponse, *oidcReqRes.OIDCErrorResponse, error) { - return RefreshFlowAndUpdate(rlog, provider, tokenID, mytoken, rt, scopes, audiences, updateChangedRTInDB) +) (*oidcreqres.OIDCTokenResponse, *oidcreqres.OIDCErrorResponse, error) { + return DoFlowAndUpdate(rlog, provider, tokenID, mytoken, rt, scopes, audiences, updateChangedRTInDB) } func updateChangedRTInDB(rlog log.Ext1FieldLogger, tokenID mtid.MTID, newRT, mytoken string) error { diff --git a/internal/oidc/revoke/revoke.go b/internal/oidc/revoke/revoke.go index 4855dc54..371d88e2 100644 --- a/internal/oidc/revoke/revoke.go +++ b/internal/oidc/revoke/revoke.go @@ -5,8 +5,8 @@ import ( "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/oidc/oidcReqRes" - "github.com/oidc-mytoken/server/shared/httpClient" + "github.com/oidc-mytoken/server/internal/oidc/oidcreqres" + "github.com/oidc-mytoken/server/shared/httpclient" pkgModel "github.com/oidc-mytoken/server/shared/model" ) @@ -15,17 +15,17 @@ func RefreshToken(rlog log.Ext1FieldLogger, provider *config.ProviderConf, rt st if provider.Endpoints.Revocation == "" { return nil } - req := oidcReqRes.NewRTRevokeRequest(rt) - httpRes, err := httpClient.Do().R(). + req := oidcreqres.NewRTRevokeRequest(rt) + httpRes, err := httpclient.Do().R(). SetBasicAuth(provider.ClientID, provider.ClientSecret). SetFormData(req.ToFormData()). - SetError(&oidcReqRes.OIDCErrorResponse{}). + SetError(&oidcreqres.OIDCErrorResponse{}). Post(provider.Endpoints.Revocation) if err != nil { rlog.WithError(err).Error() return model.ErrorToInternalServerErrorResponse(err) } - if errRes, ok := httpRes.Error().(*oidcReqRes.OIDCErrorResponse); ok && errRes != nil && errRes.Error != "" { + if errRes, ok := httpRes.Error().(*oidcreqres.OIDCErrorResponse); ok && errRes != nil && errRes.Error != "" { return &model.Response{ Status: httpRes.RawResponse.StatusCode, Response: pkgModel.OIDCError(errRes.Error, errRes.ErrorDescription), diff --git a/internal/server/apiPath/apiPath.go b/internal/server/apipath/apiPath.go similarity index 88% rename from internal/server/apiPath/apiPath.go rename to internal/server/apipath/apiPath.go index abd6ad8a..0110e96f 100644 --- a/internal/server/apiPath/apiPath.go +++ b/internal/server/apipath/apiPath.go @@ -1,4 +1,4 @@ -package apiPath +package apipath // Prefix is the api prefix path component const Prefix = "/api" diff --git a/internal/server/httpStatus/httpStatus.go b/internal/server/httpstatus/httpStatus.go similarity index 83% rename from internal/server/httpStatus/httpStatus.go rename to internal/server/httpstatus/httpStatus.go index 114c4666..de83c157 100644 --- a/internal/server/httpStatus/httpStatus.go +++ b/internal/server/httpstatus/httpStatus.go @@ -1,4 +1,4 @@ -package httpStatus +package httpstatus // Proprietary defined status codes const ( diff --git a/internal/server/middlerwares.go b/internal/server/middlerwares.go index 169ee938..a0e3ce76 100644 --- a/internal/server/middlerwares.go +++ b/internal/server/middlerwares.go @@ -20,7 +20,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/oidc-mytoken/server/internal/config" - "github.com/oidc-mytoken/server/internal/server/apiPath" + "github.com/oidc-mytoken/server/internal/server/apipath" "github.com/oidc-mytoken/server/internal/server/routes" "github.com/oidc-mytoken/server/internal/utils/fileio" "github.com/oidc-mytoken/server/internal/utils/iputils" @@ -142,7 +142,7 @@ func addCorsMiddleware(s fiber.Router) { routes.GetGeneralPaths().JWKSEndpoint, } allowedPrefixes := []string{ - apiPath.Prefix, + apipath.Prefix, "/static", } s.Use( diff --git a/internal/server/routes/routes.go b/internal/server/routes/routes.go index 3182c19d..6bf14199 100644 --- a/internal/server/routes/routes.go +++ b/internal/server/routes/routes.go @@ -2,13 +2,16 @@ package routes import ( "github.com/oidc-mytoken/server/internal/model/version" - "github.com/oidc-mytoken/server/internal/server/apiPath" + "github.com/oidc-mytoken/server/internal/server/apipath" "github.com/oidc-mytoken/server/shared/utils" ) var routes *paths +// WellknownMytokenConfiguration is the mytoken configuration path suffix const WellknownMytokenConfiguration = "/.well-known/mytoken-configuration" + +// WellknownOpenIDConfiguration is the openid configuration path suffix const WellknownOpenIDConfiguration = "/.well-known/openid-configuration" // init initializes the server route paths @@ -16,12 +19,12 @@ func init() { routes = &paths{ api: map[int]APIPaths{ 0: { - MytokenEndpoint: utils.CombineURLPath(apiPath.V0, "/token/my"), - AccessTokenEndpoint: utils.CombineURLPath(apiPath.V0, "/token/access"), - TokenInfoEndpoint: utils.CombineURLPath(apiPath.V0, "/tokeninfo"), - RevocationEndpoint: utils.CombineURLPath(apiPath.V0, "/token/revoke"), - TokenTransferEndpoint: utils.CombineURLPath(apiPath.V0, "/token/transfer"), - UserSettingEndpoint: utils.CombineURLPath(apiPath.V0, "/settings"), + MytokenEndpoint: utils.CombineURLPath(apipath.V0, "/token/my"), + AccessTokenEndpoint: utils.CombineURLPath(apipath.V0, "/token/access"), + TokenInfoEndpoint: utils.CombineURLPath(apipath.V0, "/tokeninfo"), + RevocationEndpoint: utils.CombineURLPath(apipath.V0, "/token/revoke"), + TokenTransferEndpoint: utils.CombineURLPath(apipath.V0, "/token/transfer"), + UserSettingEndpoint: utils.CombineURLPath(apipath.V0, "/settings"), }, }, other: GeneralPaths{ diff --git a/internal/server/server.go b/internal/server/server.go index 7b9cbee3..0b957c61 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -20,7 +20,7 @@ import ( "github.com/oidc-mytoken/server/internal/endpoints/consent" "github.com/oidc-mytoken/server/internal/endpoints/redirect" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/server/apiPath" + "github.com/oidc-mytoken/server/internal/server/apipath" "github.com/oidc-mytoken/server/internal/server/routes" "github.com/oidc-mytoken/server/internal/server/ssh" "github.com/oidc-mytoken/server/internal/utils/fileio" @@ -80,7 +80,7 @@ func Init() { server.Use( func(ctx *fiber.Ctx) error { path := ctx.Path() - if !strings.HasPrefix(path, apiPath.Prefix) && ctx.Accepts( + if !strings.HasPrefix(path, apipath.Prefix) && ctx.Accepts( fiber.MIMETextHTML, fiber.MIMETextHTMLCharsetUTF8, ) != "" { ctx.Status(fiber.StatusNotFound) diff --git a/internal/server/ssh/sshServer.go b/internal/server/ssh/sshServer.go index 42a4480f..972da000 100644 --- a/internal/server/ssh/sshServer.go +++ b/internal/server/ssh/sshServer.go @@ -13,7 +13,7 @@ import ( "github.com/oidc-mytoken/server/internal/config" "github.com/oidc-mytoken/server/internal/db/dbrepo/sshrepo" "github.com/oidc-mytoken/server/internal/utils/errorfmt" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" "github.com/oidc-mytoken/server/internal/utils/logger" ) @@ -21,7 +21,7 @@ func checkPubKey(ctx ssh.Context, key ssh.PublicKey) bool { sessionID := ctx.SessionID() rlog := logger.GetSSHRequestLogger(sessionID) sshUser := ctx.User() - sshUserHash := hashUtils.SHA3_512Str([]byte(sshUser)) + sshUserHash := hashutils.SHA3_512Str([]byte(sshUser)) sshKeyFP := gossh.FingerprintSHA256(key) ip := ctx.RemoteAddr().String() if addr, ok := ctx.RemoteAddr().(*net.TCPAddr); ok { diff --git a/internal/utils/auth/auther.go b/internal/utils/auth/auther.go index 34d3b10d..fa0d4504 100644 --- a/internal/utils/auth/auther.go +++ b/internal/utils/auth/auther.go @@ -9,7 +9,7 @@ import ( "github.com/oidc-mytoken/server/internal/config" dbhelper "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/mytokenrepohelper" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" model2 "github.com/oidc-mytoken/server/shared/model" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" @@ -36,7 +36,7 @@ func RequireMytoken(rlog log.Ext1FieldLogger, reqToken *universalmytoken.Univers *mytoken.Mytoken, *model.Response, ) { if reqToken.JWT == "" { - t, found := ctxUtils.GetMytoken(ctx) + t, found := ctxutils.GetMytoken(ctx) if t == nil { errDesc := "no mytoken found in request" if found { diff --git a/internal/utils/cookies/cookies.go b/internal/utils/cookies/cookies.go index f3ea113d..30f1bbaa 100644 --- a/internal/utils/cookies/cookies.go +++ b/internal/utils/cookies/cookies.go @@ -7,7 +7,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/oidc-mytoken/server/internal/config" - "github.com/oidc-mytoken/server/internal/server/apiPath" + "github.com/oidc-mytoken/server/internal/server/apipath" ) const ( @@ -37,7 +37,7 @@ func MytokenCookie(mytoken string) *fiber.Cookie { return &fiber.Cookie{ Name: mytokenCookieName, Value: mytoken, - Path: apiPath.Prefix, + Path: apipath.Prefix, Domain: cookieDomain, MaxAge: CookieLifetime, Secure: cookieSecure, @@ -51,7 +51,7 @@ func TransferCodeCookie(transferCode string, expiresIn int) *fiber.Cookie { return &fiber.Cookie{ Name: transferCodeCookieName, Value: transferCode, - Path: apiPath.Prefix, + Path: apipath.Prefix, Domain: cookieDomain, MaxAge: expiresIn, Secure: cookieSecure, diff --git a/internal/utils/ctxUtils/authHeader.go b/internal/utils/ctxutils/authHeader.go similarity index 95% rename from internal/utils/ctxUtils/authHeader.go rename to internal/utils/ctxutils/authHeader.go index 42f2651d..09893447 100644 --- a/internal/utils/ctxUtils/authHeader.go +++ b/internal/utils/ctxutils/authHeader.go @@ -1,4 +1,4 @@ -package ctxUtils +package ctxutils import ( "strings" diff --git a/internal/utils/ctxUtils/grantType.go b/internal/utils/ctxutils/grantType.go similarity index 98% rename from internal/utils/ctxUtils/grantType.go rename to internal/utils/ctxutils/grantType.go index 3bf0cbff..c64a0997 100644 --- a/internal/utils/ctxUtils/grantType.go +++ b/internal/utils/ctxutils/grantType.go @@ -1,4 +1,4 @@ -package ctxUtils +package ctxutils import ( "encoding/json" diff --git a/internal/utils/ctxUtils/networkData.go b/internal/utils/ctxutils/networkData.go similarity index 94% rename from internal/utils/ctxUtils/networkData.go rename to internal/utils/ctxutils/networkData.go index d3ef82cc..b8ae0569 100644 --- a/internal/utils/ctxUtils/networkData.go +++ b/internal/utils/ctxutils/networkData.go @@ -1,4 +1,4 @@ -package ctxUtils +package ctxutils import ( "github.com/gofiber/fiber/v2" diff --git a/internal/utils/ctxUtils/oidcFlow.go b/internal/utils/ctxutils/oidcFlow.go similarity index 98% rename from internal/utils/ctxUtils/oidcFlow.go rename to internal/utils/ctxutils/oidcFlow.go index 5d320719..1aefdb3a 100644 --- a/internal/utils/ctxUtils/oidcFlow.go +++ b/internal/utils/ctxutils/oidcFlow.go @@ -1,4 +1,4 @@ -package ctxUtils +package ctxutils import ( "encoding/json" diff --git a/internal/utils/ctxUtils/token.go b/internal/utils/ctxutils/token.go similarity index 98% rename from internal/utils/ctxUtils/token.go rename to internal/utils/ctxutils/token.go index 3c8e3301..2836f659 100644 --- a/internal/utils/ctxUtils/token.go +++ b/internal/utils/ctxutils/token.go @@ -1,4 +1,4 @@ -package ctxUtils +package ctxutils import ( "encoding/json" diff --git a/internal/utils/fileio/filesystem.go b/internal/utils/fileio/filesystem.go index 29617cc3..aac42875 100644 --- a/internal/utils/fileio/filesystem.go +++ b/internal/utils/fileio/filesystem.go @@ -12,9 +12,9 @@ import ( // Filesystems for a file returning the first one found type SearcherFilesystem []http.FileSystem -type SearcherFile []http.File +type searcherFile []http.File -func (sf SearcherFile) collectErrors(callback func(file http.File) error) error { +func (sf searcherFile) collectErrors(callback func(file http.File) error) error { err := mulerrors.Errors{} for _, f := range sf { e := callback(f) @@ -29,7 +29,7 @@ func (sf SearcherFile) collectErrors(callback func(file http.File) error) error } // Close implements the http.File interface -func (sf SearcherFile) Close() error { +func (sf searcherFile) Close() error { return sf.collectErrors( func(file http.File) error { return file.Close() @@ -38,7 +38,7 @@ func (sf SearcherFile) Close() error { } // Read implements the http.File interface -func (sf SearcherFile) Read(p []byte) (n int, err error) { +func (sf searcherFile) Read(p []byte) (n int, err error) { for _, f := range sf { buffer := make([]byte, len(p)) n, err = f.Read(buffer) @@ -51,7 +51,7 @@ func (sf SearcherFile) Read(p []byte) (n int, err error) { } // Seek implements the http.File interface -func (sf SearcherFile) Seek(offset int64, whence int) (i int64, e error) { +func (sf searcherFile) Seek(offset int64, whence int) (i int64, e error) { for _, f := range sf { i, e = f.Seek(offset, whence) if e == nil { @@ -62,7 +62,7 @@ func (sf SearcherFile) Seek(offset int64, whence int) (i int64, e error) { } // Readdir implements the http.File interface -func (sf SearcherFile) Readdir(count int) (infos []fs.FileInfo, err error) { +func (sf searcherFile) Readdir(count int) (infos []fs.FileInfo, err error) { for _, f := range sf { if count > 0 && len(infos) >= count { break @@ -91,7 +91,7 @@ func (sf SearcherFile) Readdir(count int) (infos []fs.FileInfo, err error) { } // Stat implements the http.File interface -func (sf SearcherFile) Stat() (i fs.FileInfo, e error) { +func (sf searcherFile) Stat() (i fs.FileInfo, e error) { for _, f := range sf { i, e = f.Stat() if e == nil { @@ -103,7 +103,7 @@ func (sf SearcherFile) Stat() (i fs.FileInfo, e error) { // Open implements the http.FileSystem interface func (lfs SearcherFilesystem) Open(name string) (http.File, error) { - sf := SearcherFile{} + sf := searcherFile{} for _, ffs := range lfs { if ffs == nil { continue diff --git a/internal/utils/fileio/join.go b/internal/utils/fileio/join.go index fce97229..d82a39c8 100644 --- a/internal/utils/fileio/join.go +++ b/internal/utils/fileio/join.go @@ -4,6 +4,7 @@ import ( "path/filepath" ) +// JoinIfFirstNotEmpty only joins filepaths if the first element is not empty func JoinIfFirstNotEmpty(elem ...string) string { if len(elem) > 0 && elem[0] != "" { return filepath.Join(elem...) diff --git a/internal/utils/hashUtils/hashUtils.go b/internal/utils/hashutils/hashUtils.go similarity index 97% rename from internal/utils/hashUtils/hashUtils.go rename to internal/utils/hashutils/hashUtils.go index eaf64648..ab76a0a7 100644 --- a/internal/utils/hashUtils/hashUtils.go +++ b/internal/utils/hashutils/hashUtils.go @@ -1,4 +1,4 @@ -package hashUtils +package hashutils import ( "crypto/hmac" @@ -32,6 +32,7 @@ func HMACBasedHash(data []byte) string { } // SHA3_256Str hashes the passed data with SHA3 256 +// //goland:noinspection GoSnakeCaseUsage func SHA3_256Str(data []byte) string { hash := sha3.Sum256(data) @@ -39,6 +40,7 @@ func SHA3_256Str(data []byte) string { } // SHA3_512Str hashes the passed data with SHA3 512 +// //goland:noinspection GoSnakeCaseUsage func SHA3_512Str(data []byte) string { hash := sha3.Sum512(data) diff --git a/internal/utils/hashUtils/hashUtils_test.go b/internal/utils/hashutils/hashUtils_test.go similarity index 98% rename from internal/utils/hashUtils/hashUtils_test.go rename to internal/utils/hashutils/hashUtils_test.go index a7bf7376..0e7aacac 100644 --- a/internal/utils/hashUtils/hashUtils_test.go +++ b/internal/utils/hashutils/hashUtils_test.go @@ -1,4 +1,4 @@ -package hashUtils +package hashutils import ( "testing" diff --git a/internal/utils/logger/smartlogger.go b/internal/utils/logger/smartlogger.go index 3403406a..fdca6125 100644 --- a/internal/utils/logger/smartlogger.go +++ b/internal/utils/logger/smartlogger.go @@ -12,7 +12,7 @@ import ( "github.com/oidc-mytoken/server/internal/config" ) -type SmartLogger struct { +type smartLogger struct { *log.Entry rootHook *rootHook ctx smartLoggerContext @@ -127,7 +127,7 @@ func getIDlogger(id string) log.Ext1FieldLogger { if !config.Get().Logging.Internal.Smart.Enabled { return getLogEntry(id, log.StandardLogger()) } - smartLog := &SmartLogger{ + smartLog := &smartLogger{ ctx: smartLoggerContext{ buffer: new(bytes.Buffer), id: id, @@ -149,6 +149,6 @@ func GetRequestLogger(ctx *fiber.Ctx) log.Ext1FieldLogger { } // GetSSHRequestLogger returns a logrus.Ext1FieldLogger that always includes an ssh request's id -func GetSSHRequestLogger(sessionId string) log.Ext1FieldLogger { - return getIDlogger(sessionId) +func GetSSHRequestLogger(sessionID string) log.Ext1FieldLogger { + return getIDlogger(sessionID) } diff --git a/internal/utils/templating/keys.go b/internal/utils/templating/keys.go index ac894f9a..7abb9d8c 100644 --- a/internal/utils/templating/keys.go +++ b/internal/utils/templating/keys.go @@ -30,7 +30,7 @@ const ( MustacheKeyTokenName = "token-name" MustacheKeyIss = "iss" MustacheKeySupportedScopes = "supported_scopes" - MustacheKeyInstanceUrl = "instance-url" + MustacheKeyInstanceURL = "instance-url" ) // Keys for sub configs diff --git a/internal/utils/utils.go b/internal/utils/utils.go index 22c971c7..bdc2e1c0 100644 --- a/internal/utils/utils.go +++ b/internal/utils/utils.go @@ -1,12 +1,12 @@ package utils import ( - "github.com/oidc-mytoken/server/internal/utils/hashUtils" - "github.com/oidc-mytoken/server/shared/utils/issuerUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" + "github.com/oidc-mytoken/server/shared/utils/issuerutils" ) // CreateMytokenSubject creates the subject of a Mytoken from the oidc subject and oidc issuer func CreateMytokenSubject(oidcSub, oidcIss string) string { - comb := issuerUtils.CombineSubIss(oidcSub, oidcIss) - return hashUtils.SHA3_256Str([]byte(comb)) + comb := issuerutils.CombineSubIss(oidcSub, oidcIss) + return hashutils.SHA3_256Str([]byte(comb)) } diff --git a/internal/utils/zipdownload/zipdownload.go b/internal/utils/zipdownload/zipdownload.go index 72ed6ee9..57725841 100644 --- a/internal/utils/zipdownload/zipdownload.go +++ b/internal/utils/zipdownload/zipdownload.go @@ -7,12 +7,12 @@ import ( "github.com/pkg/errors" - "github.com/oidc-mytoken/server/shared/httpClient" + "github.com/oidc-mytoken/server/shared/httpclient" ) // DownloadZipped downloads a zip archive and returns all contained files func DownloadZipped(url string) (map[string][]byte, error) { - resp, err := httpClient.Do().R().Get(url) + resp, err := httpclient.Do().R().Get(url) if err != nil { return nil, errors.WithStack(err) } diff --git a/shared/httpClient/httpClient.go b/shared/httpclient/httpClient.go similarity index 97% rename from shared/httpClient/httpClient.go rename to shared/httpclient/httpClient.go index baf1d92c..79a9783b 100644 --- a/shared/httpClient/httpClient.go +++ b/shared/httpclient/httpClient.go @@ -1,4 +1,4 @@ -package httpClient +package httpclient import ( "time" diff --git a/shared/mytoken/mytokenHandler.go b/shared/mytoken/mytokenHandler.go index a01a9900..f4d73aa1 100644 --- a/shared/mytoken/mytokenHandler.go +++ b/shared/mytoken/mytokenHandler.go @@ -12,8 +12,10 @@ import ( "github.com/oidc-mytoken/server/internal/db/dbrepo/cryptstore" "github.com/oidc-mytoken/server/internal/db/dbrepo/encryptionkeyrepo" + "github.com/oidc-mytoken/server/internal/server/httpstatus" "github.com/oidc-mytoken/server/internal/utils/auth" "github.com/oidc-mytoken/server/internal/utils/cookies" + "github.com/oidc-mytoken/server/internal/utils/ctxutils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" "github.com/oidc-mytoken/server/internal/utils/logger" "github.com/oidc-mytoken/server/shared/mytoken/rotation" @@ -29,8 +31,6 @@ import ( response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/oidc/revoke" - "github.com/oidc-mytoken/server/internal/server/httpStatus" - "github.com/oidc-mytoken/server/internal/utils/ctxUtils" pkgModel "github.com/oidc-mytoken/server/shared/model" eventService "github.com/oidc-mytoken/server/shared/mytoken/event" event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" @@ -74,7 +74,7 @@ func HandleMytokenFromTransferCode(ctx *fiber.Ctx) *model.Response { return errors.New(errResPlaceholder) } tokenStr, err = transfercoderepo.PopTokenForTransferCode( - rlog, tx, req.TransferCode, *ctxUtils.ClientMetaData(ctx), + rlog, tx, req.TransferCode, *ctxutils.ClientMetaData(ctx), ) return err }, @@ -116,7 +116,7 @@ func HandleMytokenFromMytokenReqChecks( rlog log.Ext1FieldLogger, req *response.MytokenFromMytokenRequest, ip string, ctx *fiber.Ctx, ) (*restrictions.Restriction, *mytoken.Mytoken, *model.Response) { - req.Restrictions.ReplaceThisIp(ip) + req.Restrictions.ReplaceThisIP(ip) req.Restrictions.ClearUnsupportedKeys() rlog.Trace("Parsed mytoken request") @@ -150,7 +150,7 @@ func HandleMytokenFromMytoken(ctx *fiber.Ctx) *model.Response { if errRes != nil { return errRes } - return HandleMytokenFromMytokenReq(rlog, mt, req, ctxUtils.ClientMetaData(ctx), usedRestriction) + return HandleMytokenFromMytokenReq(rlog, mt, req, ctxutils.ClientMetaData(ctx), usedRestriction) } // HandleMytokenFromMytokenReq handles a mytoken request (from an existing mytoken) @@ -315,7 +315,7 @@ func RevokeMytoken( } if strings.HasPrefix(errorfmt.Error(err), "oidc_error") { return &model.Response{ - Status: httpStatus.StatusOIDPError, + Status: httpstatus.StatusOIDPError, Response: pkgModel.OIDCError(errorfmt.Error(err), ""), } } diff --git a/shared/mytoken/pkg/mtid/mtid.go b/shared/mytoken/pkg/mtid/mtid.go index a5a4e544..c79f3149 100644 --- a/shared/mytoken/pkg/mtid/mtid.go +++ b/shared/mytoken/pkg/mtid/mtid.go @@ -8,7 +8,7 @@ import ( uuid "github.com/satori/go.uuid" "github.com/oidc-mytoken/server/internal/db" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" ) // MTID is a type for the mytoken id @@ -43,7 +43,7 @@ func (i *MTID) HashValid() bool { // Hash returns the MTID's hash func (i *MTID) Hash() string { if i.hash == "" && i.Valid() { - i.hash = hashUtils.SHA512Str(i.Bytes()) + i.hash = hashutils.SHA512Str(i.Bytes()) } return i.hash } diff --git a/shared/mytoken/restrictions/restriction.go b/shared/mytoken/restrictions/restriction.go index 9f7b3df9..2fe8be8b 100644 --- a/shared/mytoken/restrictions/restriction.go +++ b/shared/mytoken/restrictions/restriction.go @@ -8,19 +8,17 @@ import ( "github.com/jinzhu/copier" "github.com/jmoiron/sqlx" + "github.com/oidc-mytoken/api/v0" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/oidc-mytoken/server/internal/config" + "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/mytokenrepohelper" "github.com/oidc-mytoken/server/internal/model" "github.com/oidc-mytoken/server/internal/utils/errorfmt" - "github.com/oidc-mytoken/server/internal/utils/iputils" - - "github.com/oidc-mytoken/api/v0" - - "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/mytokenrepohelper" "github.com/oidc-mytoken/server/internal/utils/geoip" - "github.com/oidc-mytoken/server/internal/utils/hashUtils" + "github.com/oidc-mytoken/server/internal/utils/hashutils" + "github.com/oidc-mytoken/server/internal/utils/iputils" "github.com/oidc-mytoken/server/shared/mytoken/pkg/mtid" "github.com/oidc-mytoken/server/shared/utils" "github.com/oidc-mytoken/server/shared/utils/unixtime" @@ -107,7 +105,7 @@ func (r *Restriction) legacyHash() ([]byte, error) { if err != nil { return nil, errors.WithStack(err) } - return hashUtils.SHA512(j), nil + return hashutils.SHA512(j), nil } // hash returns the hash of this restriction @@ -116,7 +114,7 @@ func (r *Restriction) hash() ([]byte, error) { if err != nil { return nil, errors.WithStack(err) } - return hashUtils.SHA512(j), nil + return hashutils.SHA512(j), nil } func (r *Restriction) verifyNbf(now unixtime.UnixTime) bool { @@ -517,8 +515,8 @@ func Tighten(rlog log.Ext1FieldLogger, old, wanted Restrictions) (res Restrictio return } -// ReplaceThisIp replaces the special value 'this' with the given ip. -func (r *Restrictions) ReplaceThisIp(ip string) { +// ReplaceThisIP replaces the special value 'this' with the given ip. +func (r *Restrictions) ReplaceThisIP(ip string) { for _, rr := range *r { utils.ReplaceStringInSlice(&rr.Hosts, "this", ip, false) } diff --git a/shared/utils/cryptUtils/cryptUtils.go b/shared/utils/cryptutils/cryptUtils.go similarity index 99% rename from shared/utils/cryptUtils/cryptUtils.go rename to shared/utils/cryptutils/cryptUtils.go index 7cb1acce..b520d9f1 100644 --- a/shared/utils/cryptUtils/cryptUtils.go +++ b/shared/utils/cryptutils/cryptUtils.go @@ -1,4 +1,4 @@ -package cryptUtils +package cryptutils import ( "crypto/aes" diff --git a/shared/utils/issuerUtils/issuerUtils.go b/shared/utils/issuerutils/issuerUtils.go similarity index 91% rename from shared/utils/issuerUtils/issuerUtils.go rename to shared/utils/issuerutils/issuerUtils.go index 8639e70e..955603a1 100644 --- a/shared/utils/issuerUtils/issuerUtils.go +++ b/shared/utils/issuerutils/issuerUtils.go @@ -1,4 +1,4 @@ -package issuerUtils +package issuerutils import ( "fmt" @@ -13,10 +13,9 @@ func GetIssuerWithAndWithoutSlash(iss string) (string, string) { if strings.HasSuffix(iss0, "/") { iss1 = strings.TrimSuffix(iss0, "/") return iss1, iss0 - } else { - iss1 = fmt.Sprintf("%s%c", iss0, '/') - return iss0, iss1 } + iss1 = fmt.Sprintf("%s%c", iss0, '/') + return iss0, iss1 } // CompareIssuerURLs compares two issuer urls. Issuer urls are also accepted as diff --git a/shared/utils/issuerUtils/issuerUtils_test.go b/shared/utils/issuerutils/issuerUtils_test.go similarity index 99% rename from shared/utils/issuerUtils/issuerUtils_test.go rename to shared/utils/issuerutils/issuerUtils_test.go index e633aca0..df90ef86 100644 --- a/shared/utils/issuerUtils/issuerUtils_test.go +++ b/shared/utils/issuerutils/issuerUtils_test.go @@ -1,4 +1,4 @@ -package issuerUtils +package issuerutils import "testing" From 6cbbda3bf1778952918f5e6b2e4c79d557b19345 Mon Sep 17 00:00:00 2001 From: zachmann Date: Wed, 2 Nov 2022 12:10:26 +0100 Subject: [PATCH 23/55] [ci] add upload packages --- .../goreleaser.sh | 0 .gitlab-ci-scripts/upload.sh | 35 +++++++++++++++++++ .gitlab-ci.yml | 7 ++-- 3 files changed, 39 insertions(+), 3 deletions(-) rename .gitlab-ci-script.sh => .gitlab-ci-scripts/goreleaser.sh (100%) create mode 100755 .gitlab-ci-scripts/upload.sh diff --git a/.gitlab-ci-script.sh b/.gitlab-ci-scripts/goreleaser.sh similarity index 100% rename from .gitlab-ci-script.sh rename to .gitlab-ci-scripts/goreleaser.sh diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh new file mode 100755 index 00000000..5134fc90 --- /dev/null +++ b/.gitlab-ci-scripts/upload.sh @@ -0,0 +1,35 @@ +# ssh-key-script +[ -e /tmp/ssh-private-keys/${REPO_USER} ] && { + eval $(ssh-agent -s) + cat /tmp/ssh-private-keys/${REPO_USER} | tr -d '\r' | ssh-add - + test -d ~/.ssh || mkdir -p ~/.ssh + chmod 700 ~/.ssh +} +[ -e /tmp/ssh-private-keys/known_hosts ] && { + test -d ~/.ssh || mkdir -p ~/.ssh + cp /tmp/ssh-private-keys/known_hosts ~/.ssh/known_hosts + chmod 644 ~/.ssh/known_hosts +} +ssh-add -l +ssh -o StrictHostKeyChecking=no "${REPO_USER}@${REPO_HOST}" "hostname -f" + +# sign-repo function +sign-repos() { + ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/sign-all-repos.sh -t ${REPO_TARGET}" +} + +upload-files() { + UPLOAD_DIR=/tmp/package-upload + ssh "${REPO_USER}@${REPO_HOST}" "rm -rf $UPLOAD_DIR && mkdir -p $UPLOAD_DIR" + scp "${REPO_USER}@${REPO_HOST}" results/* $UPLOAD_DIR +} + +distribute-files() { + ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/distribute-local-packages.sh -t ${REPO_TARGET}" -w mytoken +} + + +# upload and sign +upload-files +distribute-files +sign-repos diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ecb8765..5a330124 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,7 @@ default: paths: - .cache + before_script: - mkdir -p .cache - export GOPATH=${CI_PROJECT_DIR}/.cache @@ -67,9 +68,9 @@ prerelease: GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd - REPO_BASE: /var/www/prerel - REPO_SIGN_KEY: F35D41344946F23D5D77AFD1E89B3987AEB6032B + REPO_TARGET: /prerel script: - - ./.gitlab-ci-script.sh + - .gitlab-ci-scripts/goreleaser.sh + - .gitlab-ci-scripts/upload.sh after_script: - curl -d "repo=github.com/oidc-mytoken/server" https://goreportcard.com/checks From 5d3255b26f27a85d042737bb0ab9abd41e28d5e9 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 14:23:54 +0100 Subject: [PATCH 24/55] [ci] improve release/prerelease --- .gitlab-ci-scripts/upload.sh | 4 ++++ .gitlab-ci.yml | 1 - .goreleaser-release.yml | 22 +++++++++++++--------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 5134fc90..45ecda41 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -1,3 +1,7 @@ + +#REPO_TARGET="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '/'; else echo '/prerel'; fi)" +REPO_TARGET="/prerel" + # ssh-key-script [ -e /tmp/ssh-private-keys/${REPO_USER} ] && { eval $(ssh-agent -s) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5a330124..0fdd5b40 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,7 +68,6 @@ prerelease: GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd - REPO_TARGET: /prerel script: - .gitlab-ci-scripts/goreleaser.sh - .gitlab-ci-scripts/upload.sh diff --git a/.goreleaser-release.yml b/.goreleaser-release.yml index a2b1c0cc..61e18305 100644 --- a/.goreleaser-release.yml +++ b/.goreleaser-release.yml @@ -11,8 +11,8 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' - id: setup main: ./cmd/mytoken-server/mytoken-setup @@ -21,8 +21,8 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' - id: migratedb main: ./cmd/mytoken-server/mytoken-migratedb @@ -31,8 +31,8 @@ builds: - CGO_ENABLED=0 goos: - linux - gcflags: - - all=-trimpath={{.Env.GOPATH}} + flags: + - -trimpath mod_timestamp: '{{ .CommitTimestamp }}' archives: - replacements: @@ -50,7 +50,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - server - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: Mytoken is a central web service with the goal to easily obtain OpenID Connect access tokens across devices. license: MIT @@ -80,7 +80,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - setup - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: A setup utility for the mytoken-server license: MIT @@ -102,7 +102,7 @@ nfpms: file_name_template: "{{ .PackageName }}_{{ .Version }}_{{ .Arch }}" builds: - migratedb - homepage: https://mytoken-doc.data.kit.edu/server/intro + homepage: https://mytoken-docs.data.kit.edu/server maintainer: Gabriel Zachmann description: A tool for migrating the database between versions license: MIT @@ -173,7 +173,11 @@ checksum: snapshot: name_template: "{{ .Tag }}-next" release: + prerelease: auto draft: true + github: + owner: oidc-mytoken + name: server name_template: "{{.ProjectName}} {{.Version}}" changelog: sort: asc From c2ca5ff093fa360821367acf7cb008adb09790f9 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 14:35:26 +0100 Subject: [PATCH 25/55] [ci] fix git dirty state because of .cache --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 56f6bfc7..77f89f02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ /.idea/ +/.cache/ /mytoken /mytoken-setup /mytoken-server From 5fa719bd473e7200655a0d89ff812ba6c79fa8e5 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 14:45:35 +0100 Subject: [PATCH 26/55] [ci] fix upload script --- .gitlab-ci-scripts/upload.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 45ecda41..9f3ab303 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -18,22 +18,22 @@ ssh-add -l ssh -o StrictHostKeyChecking=no "${REPO_USER}@${REPO_HOST}" "hostname -f" # sign-repo function -sign-repos() { +sign_repos() { ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/sign-all-repos.sh -t ${REPO_TARGET}" } -upload-files() { +upload_files() { UPLOAD_DIR=/tmp/package-upload ssh "${REPO_USER}@${REPO_HOST}" "rm -rf $UPLOAD_DIR && mkdir -p $UPLOAD_DIR" scp "${REPO_USER}@${REPO_HOST}" results/* $UPLOAD_DIR } -distribute-files() { +distribute_files() { ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/distribute-local-packages.sh -t ${REPO_TARGET}" -w mytoken } # upload and sign -upload-files -distribute-files -sign-repos +upload_files +distribute_files +sign_repos From cd92764af4a2e9f1898982a96f18b29cbfb177a3 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 14:57:49 +0100 Subject: [PATCH 27/55] [ci] fix scp upload --- .gitlab-ci-scripts/upload.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 9f3ab303..6695376b 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -25,11 +25,11 @@ sign_repos() { upload_files() { UPLOAD_DIR=/tmp/package-upload ssh "${REPO_USER}@${REPO_HOST}" "rm -rf $UPLOAD_DIR && mkdir -p $UPLOAD_DIR" - scp "${REPO_USER}@${REPO_HOST}" results/* $UPLOAD_DIR + scp results/* "${REPO_USER}@${REPO_HOST}:${UPLOAD_DIR}" } distribute_files() { - ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/distribute-local-packages.sh -t ${REPO_TARGET}" -w mytoken + ssh "${REPO_USER}@${REPO_HOST}" "~/ci-voodoo/ci-tools/distribute-local-packages.sh -t ${REPO_TARGET} -w mytoken" } From 33d6bda9f8f9977d5d486fb62fdab05fe1825b2e Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 15:03:13 +0100 Subject: [PATCH 28/55] [ci] fix go report --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0fdd5b40..b9bcf27e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -72,4 +72,4 @@ prerelease: - .gitlab-ci-scripts/goreleaser.sh - .gitlab-ci-scripts/upload.sh after_script: - - curl -d "repo=github.com/oidc-mytoken/server" https://goreportcard.com/checks + - docker run --rm curlimages/curl -d "repo=github.com/oidc-mytoken/server" https://goreportcard.com/checks From 704f5e2ad8c81c5aa83b85655ca44408ae082b64 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 4 Nov 2022 10:07:11 +0100 Subject: [PATCH 29/55] [ci] remove github actions, since they moved to gitlab ci --- .github/workflows/go.yml | 42 ---------------------------------- .github/workflows/release.yaml | 37 ------------------------------ 2 files changed, 79 deletions(-) delete mode 100644 .github/workflows/go.yml delete mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml deleted file mode 100644 index 0c4158c0..00000000 --- a/.github/workflows/go.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Go - -on: - push: - branches: [ master, prerelease ] - pull_request: - branches: [ master, prerelease ] - -jobs: - - build: - name: Build - runs-on: ubuntu-latest - steps: - - - name: Set up Go 1.19 - uses: actions/setup-go@v2 - with: - go-version: 1.19 - id: go - - - name: Check out code into the Go module directory - uses: actions/checkout@v2 - - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi - - - - name: Build Server Setup - run: go build -v github.com/oidc-mytoken/server/cmd/mytoken-server/mytoken-setup - - - name: Build Server - run: go build -v github.com/oidc-mytoken/server/cmd/mytoken-server - - - name: Test - run: go test -v ./... - diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml deleted file mode 100644 index 91099a67..00000000 --- a/.github/workflows/release.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: goreleaser -on: - push: - tags: - - "v*.*.*" - -jobs: - goreleaser: - runs-on: ubuntu-latest - steps: - - - name: Checkout - uses: actions/checkout@v2 - with: - fetch-depth: 0 - - - name: Set up Go - uses: actions/setup-go@v2 - with: - go-version: 1.16 - - - name: Docker Login - uses: docker/login-action@v1.12.0 - with: - # Server address of Docker registry. If not set then will default to Docker Hub - # registry: # optional - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v2 - with: - version: latest - args: release --rm-dist - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 61647303894c257f35d3c5a40d31fb9f31aaa1c5 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 09:47:25 +0100 Subject: [PATCH 30/55] add warning message to tokeninfo to indicate users must copy mytoken; close #156 --- internal/server/web/partials/tokeninfo.mustache | 7 +++++++ internal/server/web/static/js/tokeninfo-status.js | 9 ++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/internal/server/web/partials/tokeninfo.mustache b/internal/server/web/partials/tokeninfo.mustache index 87a127ac..f32dbac8 100644 --- a/internal/server/web/partials/tokeninfo.mustache +++ b/internal/server/web/partials/tokeninfo.mustache @@ -58,6 +58,13 @@
+ +
+ +

Do not loose access to your mytoken!

+

To keep access to your mytoken you must copy it to a safe location, as + you will not be able to retrieve it again from this website.

+
diff --git a/internal/server/web/static/js/tokeninfo-status.js b/internal/server/web/static/js/tokeninfo-status.js index 6b6505fe..5e560a04 100644 --- a/internal/server/web/static/js/tokeninfo-status.js +++ b/internal/server/web/static/js/tokeninfo-status.js @@ -12,16 +12,23 @@ const $tokeninfoBadgeIat = $('#tokeninfo-token-iat'); const $tokeninfoBadgeExp = $('#tokeninfo-token-exp'); const $tokeninfoBadgeIatDate = $('#tokeninfo-token-iat-date'); const $tokeninfoBadgeExpDate = $('#tokeninfo-token-exp-date'); - const $tokeninfoTypeBadges = $('.tokeninfo-token-type'); +const $tokeninfoTokenGoneWarningMsg = $('#token-gone-warning'); +$('#tokeninfo-token-copy').on('click', function () { + if (!$tokeninfoTokenGoneWarningMsg.hasClass('d-none')) { + $tokeninfoTokenGoneWarningMsg.alert('close'); + } +}); async function update_tokeninfo() { let token = storagePop('tokeninfo_token', true); if (token === "") { token = $tokenInput.val(); + $tokeninfoTokenGoneWarningMsg.hideB(); } else { $tokenInput.val(token); + $tokeninfoTokenGoneWarningMsg.showB(); } let payload = {}; if (token === "") { From f51a4dd0e2dda4b171c075e69df84e8d9694f6c9 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 3 Nov 2022 14:27:25 +0100 Subject: [PATCH 31/55] changelog --- CHANGELOG.md | 4 +++- internal/model/version/version.go | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff4baf39..e947edff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ -## mytoken 0.6.1-a +## mytoken 0.6.1-b ### API @@ -27,6 +27,8 @@ ### Enhancements - Location restriction can now be done with host names, not only plain ip addresses, see above for more details. +- Webinterface: Added message to tokeninfo after MT creation and TC exchange to indicate that users must copy the + mytoken to persist it. - Improved code quality ### Bugfixes diff --git a/internal/model/version/version.go b/internal/model/version/version.go index fe393f4f..4fe53261 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -9,7 +9,7 @@ const ( MAJOR = 0 MINOR = 6 FIX = 1 - DEV = "a" + DEV = "b" ) var version = fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, FIX) From 23fe0923e3f9993fd7b9839d50d331d9655bb590 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 4 Nov 2022 12:37:26 +0100 Subject: [PATCH 32/55] [ci] on normal release upload to prod and dont sign --- .gitlab-ci-scripts/upload.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 6695376b..1fd8c4b9 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -1,6 +1,6 @@ -#REPO_TARGET="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '/'; else echo '/prerel'; fi)" -REPO_TARGET="/prerel" +REPO_TARGET="$(if echo "$CI_COMMIT_TAG" | grep -q '-'; then echo '/prerel'; else echo '/'; fi)" +#REPO_TARGET="/prerel" # ssh-key-script [ -e /tmp/ssh-private-keys/${REPO_USER} ] && { @@ -36,4 +36,4 @@ distribute_files() { # upload and sign upload_files distribute_files -sign_repos +echo "$CI_COMMIT_TAG" | grep -q '-' && sign_repos From c9a4567d73d5ecf4de84e1ed28df35f1ff0d2438 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 4 Nov 2022 12:45:58 +0100 Subject: [PATCH 33/55] set version --- CHANGELOG.md | 2 +- internal/model/version/version.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e947edff..9602e20f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ -## mytoken 0.6.1-b +## mytoken 0.6.1 ### API diff --git a/internal/model/version/version.go b/internal/model/version/version.go index 4fe53261..cd958af1 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -9,7 +9,7 @@ const ( MAJOR = 0 MINOR = 6 FIX = 1 - DEV = "b" + DEV = "" ) var version = fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, FIX) From a10d5ee6d45562ec1f5ad5a3bd59d443ddf3823c Mon Sep 17 00:00:00 2001 From: zachmann Date: Mon, 7 Nov 2022 11:04:42 +0100 Subject: [PATCH 34/55] fix typo --- CHANGELOG.md | 2 +- internal/model/version/version.go | 2 +- internal/server/web/partials/tokeninfo.mustache | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9602e20f..7d510518 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ -## mytoken 0.6.1 +## mytoken 0.6.1-c ### API diff --git a/internal/model/version/version.go b/internal/model/version/version.go index cd958af1..a8b6fed1 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -9,7 +9,7 @@ const ( MAJOR = 0 MINOR = 6 FIX = 1 - DEV = "" + DEV = "c" ) var version = fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, FIX) diff --git a/internal/server/web/partials/tokeninfo.mustache b/internal/server/web/partials/tokeninfo.mustache index f32dbac8..f505eaae 100644 --- a/internal/server/web/partials/tokeninfo.mustache +++ b/internal/server/web/partials/tokeninfo.mustache @@ -61,7 +61,7 @@
-

Do not loose access to your mytoken!

+

Do not lose access to your mytoken!

To keep access to your mytoken you must copy it to a safe location, as you will not be able to retrieve it again from this website.

From ec35830a316ce7ed155be73bff40cf0cc0227aa6 Mon Sep 17 00:00:00 2001 From: zachmann Date: Mon, 7 Nov 2022 11:05:09 +0100 Subject: [PATCH 35/55] [ci] only push to prerel repo --- .gitlab-ci-scripts/upload.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 1fd8c4b9..40bca617 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -1,6 +1,6 @@ -REPO_TARGET="$(if echo "$CI_COMMIT_TAG" | grep -q '-'; then echo '/prerel'; else echo '/'; fi)" -#REPO_TARGET="/prerel" +#REPO_TARGET="$(if echo "$CI_COMMIT_TAG" | grep -q '-'; then echo '/prerel'; else echo '/'; fi)" +REPO_TARGET="/prerel" # ssh-key-script [ -e /tmp/ssh-private-keys/${REPO_USER} ] && { @@ -36,4 +36,5 @@ distribute_files() { # upload and sign upload_files distribute_files -echo "$CI_COMMIT_TAG" | grep -q '-' && sign_repos +sign_repos +#echo "$CI_COMMIT_TAG" | grep -q '-' && sign_repos From f2ac3463b7045831f5455f83faf6e65d4fe2d191 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 10 Nov 2022 08:40:14 +0100 Subject: [PATCH 36/55] adapt new versioning procedure --- .gitignore | 1 + .gitlab-ci-scripts/set-prerel-version | 12 ++++++ cmd/mytoken-server/main.go | 2 +- cmd/mytoken-server/mytoken-migratedb/main.go | 2 +- .../mytoken-migratedb/version.go | 2 +- cmd/mytoken-server/mytoken-setup/setup.go | 2 +- internal/db/dbrepo/versionrepo/versionrepo.go | 2 +- .../configuration/configurationEndpoint.go | 2 +- internal/model/version/VERSION | 1 + internal/model/version/version.go | 40 ++++++++++++------ .../server/web/static/img/mytoken-grey.png | Bin 8965 -> 10619 bytes internal/server/web/static/img/mytoken.png | Bin 8987 -> 10614 bytes mytoken.png | Bin 8987 -> 10614 bytes 13 files changed, 46 insertions(+), 20 deletions(-) create mode 100755 .gitlab-ci-scripts/set-prerel-version create mode 100644 internal/model/version/VERSION diff --git a/.gitignore b/.gitignore index 77f89f02..c947f0fd 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ dist/ /docker/db.env /docker/haproxy/haproxy.cfg /docker/.env +/results/ diff --git a/.gitlab-ci-scripts/set-prerel-version b/.gitlab-ci-scripts/set-prerel-version new file mode 100755 index 00000000..db51627c --- /dev/null +++ b/.gitlab-ci-scripts/set-prerel-version @@ -0,0 +1,12 @@ +#!/bin/bash + +MASTER_BRANCH=master +VERSION_FILE=internal/model/version/VERSION + +PREREL=$(git rev-list --count HEAD ^$MASTER_BRANCH) +VERSION=$(cat $VERSION_FILE) +FULL_VERSION="${VERSION}-pr${PREREL}" +echo "$FULL_VERSION" > $VERSION_FILE +git add $VERSION_FILE +git commit -m "dummy prerel version" +git tag "v${FULL_VERSION}" \ No newline at end of file diff --git a/cmd/mytoken-server/main.go b/cmd/mytoken-server/main.go index aa7e438d..9fd35950 100644 --- a/cmd/mytoken-server/main.go +++ b/cmd/mytoken-server/main.go @@ -33,7 +33,7 @@ func main() { authcode.Init() versionrepo.ConnectToVersion() jws.LoadKey() - httpclient.Init(config.Get().IssuerURL, fmt.Sprintf("mytoken-server %s", version.VERSION())) + httpclient.Init(config.Get().IssuerURL, fmt.Sprintf("mytoken-server %s", version.VERSION)) geoip.Init() settings.InitSettings() cookies.Init() diff --git a/cmd/mytoken-server/mytoken-migratedb/main.go b/cmd/mytoken-server/mytoken-migratedb/main.go index 9c61b863..6d66488b 100644 --- a/cmd/mytoken-server/mytoken-migratedb/main.go +++ b/cmd/mytoken-server/mytoken-migratedb/main.go @@ -28,7 +28,7 @@ var dbConfig struct { var app = &cli.App{ Name: "mytoken-migratedb", Usage: "Command line client for easy database migration between mytoken versions", - Version: version.VERSION(), + Version: version.VERSION, Compiled: time.Time{}, Authors: []*cli.Author{ { diff --git a/cmd/mytoken-server/mytoken-migratedb/version.go b/cmd/mytoken-server/mytoken-migratedb/version.go index 6811f45a..725e1488 100644 --- a/cmd/mytoken-server/mytoken-migratedb/version.go +++ b/cmd/mytoken-server/mytoken-migratedb/version.go @@ -40,7 +40,7 @@ func getDoneMap(state versionrepo.DBVersionState) (map[string]bool, map[string]b } func migrateDB(mytokenNodes []string) error { - v := "v" + version.VERSION() + v := "v" + version.VERSION dbState, err := versionrepo.GetVersionState(log.StandardLogger(), nil) if err != nil { return err diff --git a/cmd/mytoken-server/mytoken-setup/setup.go b/cmd/mytoken-server/mytoken-setup/setup.go index 97c38a99..000a2de9 100644 --- a/cmd/mytoken-server/mytoken-setup/setup.go +++ b/cmd/mytoken-server/mytoken-setup/setup.go @@ -86,7 +86,7 @@ var sigKeyFile string var app = &cli.App{ Name: "mytoken-setup", Usage: "Command line client for easily setting up a mytoken server", - Version: version.VERSION(), + Version: version.VERSION, Compiled: time.Time{}, Authors: []*cli.Author{ { diff --git a/internal/db/dbrepo/versionrepo/versionrepo.go b/internal/db/dbrepo/versionrepo/versionrepo.go index 3fdce40a..0c8ef569 100644 --- a/internal/db/dbrepo/versionrepo/versionrepo.go +++ b/internal/db/dbrepo/versionrepo/versionrepo.go @@ -126,7 +126,7 @@ func ConnectToVersion() { if hasAllVersions, missingVersions := state.dBHasAllVersions(); !hasAllVersions { log.WithFields( log.Fields{ - "server_version": version.VERSION(), + "server_version": version.VERSION, "missing_versions_in_db": missingVersions, }, ).Fatal("database schema not updated to this server version") diff --git a/internal/endpoints/configuration/configurationEndpoint.go b/internal/endpoints/configuration/configurationEndpoint.go index 2287d755..1553f569 100644 --- a/internal/endpoints/configuration/configurationEndpoint.go +++ b/internal/endpoints/configuration/configurationEndpoint.go @@ -62,7 +62,7 @@ func basicConfiguration() *pkg.MytokenConfiguration { ProvidersSupported: getProvidersFromConfig(), TokenSigningAlgValue: config.Get().Signing.Alg, ServiceDocumentation: config.Get().ServiceDocumentation, - Version: version.VERSION(), + Version: version.VERSION, }, AccessTokenEndpointGrantTypesSupported: []pkgModel.GrantType{pkgModel.GrantTypeMytoken}, MytokenEndpointGrantTypesSupported: []pkgModel.GrantType{ diff --git a/internal/model/version/VERSION b/internal/model/version/VERSION new file mode 100644 index 00000000..7ceb0404 --- /dev/null +++ b/internal/model/version/VERSION @@ -0,0 +1 @@ +0.6.1 \ No newline at end of file diff --git a/internal/model/version/version.go b/internal/model/version/version.go index a8b6fed1..1ba812ad 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -1,24 +1,36 @@ package version import ( - "fmt" + _ "embed" + "strconv" + "strings" ) +//go:embed VERSION +var VERSION string + // Version segments -const ( - MAJOR = 0 - MINOR = 6 - FIX = 1 - DEV = "c" +var ( + MAJOR int + MINOR int + FIX int + PRE int ) -var version = fmt.Sprintf("%d.%d.%d", MAJOR, MINOR, FIX) -var devVersion = fmt.Sprintf("%s-%s", version, DEV) - -// VERSION returns the current mytoken version -func VERSION() string { - if DEV != "" { - return devVersion +func init() { + if VERSION[len(VERSION)-1] == '\n' { + VERSION = VERSION[:len(VERSION)-1] + } + v := strings.Split(VERSION, ".") + MAJOR, _ = strconv.Atoi(v[0]) + MINOR, _ = strconv.Atoi(v[1]) + ps := strings.Split(v[2], "-") + FIX, _ = strconv.Atoi(ps[0]) + if len(ps) > 1 { + pre := ps[1] + if strings.HasPrefix(pre, "pr") { + pre = pre[2:] + } + PRE, _ = strconv.Atoi(pre) } - return version } diff --git a/internal/server/web/static/img/mytoken-grey.png b/internal/server/web/static/img/mytoken-grey.png index 3e4e9e0ec31568cb18aa1c60eb00ee1e5cdd32d1..07e1767e5bf907eee6c8d2a530bfd9cc542002e7 100644 GIT binary patch literal 10619 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 literal 8987 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 literal 8987 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 Date: Thu, 10 Nov 2022 14:55:47 +0100 Subject: [PATCH 37/55] [ci] adapt goreleaser --- .gitlab-ci-scripts/goreleaser.sh | 9 ++++++--- .gitlab-ci.yml | 5 ++++- .goreleaser-release.yml | 6 +++--- .goreleaser.yml | 6 +++--- CHANGELOG.md | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci-scripts/goreleaser.sh b/.gitlab-ci-scripts/goreleaser.sh index 38e47bb9..15e36509 100755 --- a/.gitlab-ci-scripts/goreleaser.sh +++ b/.gitlab-ci-scripts/goreleaser.sh @@ -2,11 +2,14 @@ mkdir ../shared first=$(grep '^## ' -nm1 CHANGELOG.md | cut -d':' -f1); \ second=$(grep '^## ' -nm2 CHANGELOG.md | tail -n1 | cut -d':' -f1); \ tail -n+$first CHANGELOG.md | head -n$(($second-$first)) > ../shared/release.md -GORELEASER_CONFIG="$(if echo $CI_COMMIT_TAG | grep -q '-'; then echo '.goreleaser.yml'; else echo '.goreleaser-release.yml'; fi)" +GORELEASER_CONFIG=".goreleaser.yml" +if [ -z "$CI_COMMIT_TAG" ] || echo "$CI_COMMIT_TAG" | grep -qv; then +GORELEASER_CONFIG=".goreleaser-release.yml" +fi BASEDIR=/go/src/github.com/oidc-mytoken/server docker run --rm --privileged \ - -v $PWD:$BASEDIR \ - -w $BASEDIR\ + -v "$PWD":"$BASEDIR" \ + -w "$BASEDIR" \ -v "${PWD}/../shared":/tmp/shared \ -v /var/run/docker.sock:/var/run/docker.sock \ -e DOCKER_USERNAME -e DOCKER_PASSWORD \ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b9bcf27e..0a93a54b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -61,7 +61,9 @@ prerelease: services: - docker:dind only: - - tags + refs: + - tags + - prerel tags: - linux variables: @@ -69,6 +71,7 @@ prerelease: REPO_HOST: repo.data.kit.edu REPO_USER: cicd script: + - .gitlab-ci-scripts/set-prerel-version - .gitlab-ci-scripts/goreleaser.sh - .gitlab-ci-scripts/upload.sh after_script: diff --git a/.goreleaser-release.yml b/.goreleaser-release.yml index 61e18305..d535f336 100644 --- a/.goreleaser-release.yml +++ b/.goreleaser-release.yml @@ -57,7 +57,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin contents: @@ -87,7 +87,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin overrides: @@ -111,7 +111,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin overrides: diff --git a/.goreleaser.yml b/.goreleaser.yml index 0b296b0c..e8b3fa48 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -57,7 +57,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin contents: @@ -87,7 +87,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin overrides: @@ -111,7 +111,7 @@ nfpms: formats: - deb - rpm - release: 1 + release: "1" section: misc bindir: /usr/bin overrides: diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d510518..9602e20f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ -## mytoken 0.6.1-c +## mytoken 0.6.1 ### API From 1722953c9ff1692bed532788da5d2811683e5009 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 10 Nov 2022 14:59:53 +0100 Subject: [PATCH 38/55] [lint] fix linting --- internal/model/version/version.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/model/version/version.go b/internal/model/version/version.go index 1ba812ad..e6952655 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -1,11 +1,13 @@ package version import ( - _ "embed" + _ "embed" // for go:embed "strconv" "strings" ) +// VERSION holds the server's version +// //go:embed VERSION var VERSION string From 4e2b5e9bba7d601fed44d63b6a2db08a785789e8 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 10 Nov 2022 15:08:53 +0100 Subject: [PATCH 39/55] [ci] fix set-prerel-version calling --- .gitlab-ci-scripts/set-prerel-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci-scripts/set-prerel-version b/.gitlab-ci-scripts/set-prerel-version index db51627c..f26ee614 100755 --- a/.gitlab-ci-scripts/set-prerel-version +++ b/.gitlab-ci-scripts/set-prerel-version @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh MASTER_BRANCH=master VERSION_FILE=internal/model/version/VERSION From dd0cc5fdb12dd4ccb76ff7237215c156d0386c05 Mon Sep 17 00:00:00 2001 From: zachmann Date: Thu, 10 Nov 2022 15:28:24 +0100 Subject: [PATCH 40/55] [ci] fix set-prerel-version script --- .gitlab-ci-scripts/set-prerel-version | 4 ++++ .gitlab-ci.yml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci-scripts/set-prerel-version b/.gitlab-ci-scripts/set-prerel-version index f26ee614..a1e598ca 100755 --- a/.gitlab-ci-scripts/set-prerel-version +++ b/.gitlab-ci-scripts/set-prerel-version @@ -3,6 +3,10 @@ MASTER_BRANCH=master VERSION_FILE=internal/model/version/VERSION +git config --global --add safe.directory "$PWD" +git config user.email "ci@repo.data.kit.edu" +git config user.name "cicd" + PREREL=$(git rev-list --count HEAD ^$MASTER_BRANCH) VERSION=$(cat $VERSION_FILE) FULL_VERSION="${VERSION}-pr${PREREL}" diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0a93a54b..cbb460ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -71,7 +71,7 @@ prerelease: REPO_HOST: repo.data.kit.edu REPO_USER: cicd script: - - .gitlab-ci-scripts/set-prerel-version + - docker run --rm -v $PWD:/tmp/mytoken -w /tmp/mytoken bitnami/git .gitlab-ci-scripts/set-prerel-version - .gitlab-ci-scripts/goreleaser.sh - .gitlab-ci-scripts/upload.sh after_script: From a38edf42627321cb0d2cdfcb8ad58103415d9281 Mon Sep 17 00:00:00 2001 From: cicd Date: Thu, 10 Nov 2022 14:32:04 +0000 Subject: [PATCH 41/55] dummy prerel version --- internal/model/version/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/model/version/VERSION b/internal/model/version/VERSION index 7ceb0404..2da5b84f 100644 --- a/internal/model/version/VERSION +++ b/internal/model/version/VERSION @@ -1 +1 @@ -0.6.1 \ No newline at end of file +0.6.1-pr41 From 55fe64fa79b43907113472c715564245acc38e9c Mon Sep 17 00:00:00 2001 From: cicd Date: Thu, 10 Nov 2022 15:36:18 +0100 Subject: [PATCH 42/55] [ci] debug git depth --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cbb460ad..47b57bce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -67,7 +67,7 @@ prerelease: tags: - linux variables: - GIT_DEPTH: 0 +# GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd script: From f0856dbc3d8b13ec7f9b3ae0fa19d5d1490552ae Mon Sep 17 00:00:00 2001 From: cicd Date: Thu, 10 Nov 2022 15:43:50 +0100 Subject: [PATCH 43/55] [ci] debug git strategy --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 47b57bce..f4db3e52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -67,7 +67,8 @@ prerelease: tags: - linux variables: -# GIT_DEPTH: 0 + GIT_STRATEGY: clone + GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd script: From 7ad4275e2f08b049c3ad7301382ec732cef63920 Mon Sep 17 00:00:00 2001 From: cicd Date: Thu, 10 Nov 2022 15:44:50 +0100 Subject: [PATCH 44/55] [ci] fix version file --- internal/model/version/VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/model/version/VERSION b/internal/model/version/VERSION index 2da5b84f..ee6cdce3 100644 --- a/internal/model/version/VERSION +++ b/internal/model/version/VERSION @@ -1 +1 @@ -0.6.1-pr41 +0.6.1 From c5ff0a651399679983fda1f750af2c17fdab2d79 Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:32:21 +0100 Subject: [PATCH 45/55] [ci] debug git --- .gitlab-ci-scripts/set-prerel-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci-scripts/set-prerel-version b/.gitlab-ci-scripts/set-prerel-version index a1e598ca..a756819a 100755 --- a/.gitlab-ci-scripts/set-prerel-version +++ b/.gitlab-ci-scripts/set-prerel-version @@ -1,6 +1,6 @@ #!/bin/sh -MASTER_BRANCH=master +MASTER_BRANCH=refs/remotes/origin/master VERSION_FILE=internal/model/version/VERSION git config --global --add safe.directory "$PWD" From ed2eb065ef88d4c21a45358776f8947ae9a7ab7e Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:35:50 +0100 Subject: [PATCH 46/55] [ci] debug git --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f4db3e52..cbb460ad 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -67,7 +67,6 @@ prerelease: tags: - linux variables: - GIT_STRATEGY: clone GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd From 7611a217046a6bd3b6dcf2a93b886872f6bf2e18 Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:46:45 +0100 Subject: [PATCH 47/55] [ci] fix git strategy --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index cbb460ad..f4db3e52 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -67,6 +67,7 @@ prerelease: tags: - linux variables: + GIT_STRATEGY: clone GIT_DEPTH: 0 REPO_HOST: repo.data.kit.edu REPO_USER: cicd From d752b34884ee9d13269ef0fe87aa7c079023229d Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:47:07 +0100 Subject: [PATCH 48/55] [ci] dont create release on prerelease --- .goreleaser.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.goreleaser.yml b/.goreleaser.yml index e8b3fa48..9827db9a 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -167,7 +167,7 @@ checksum: snapshot: name_template: "{{ .Tag }}-next" release: - # disable: true + disable: true prerelease: auto draft: true github: From 198120e796b2f8cc36345a96791be534d66995a7 Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:47:23 +0100 Subject: [PATCH 49/55] [ci] fix usage of wrong goreleaser config --- .gitlab-ci-scripts/goreleaser.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci-scripts/goreleaser.sh b/.gitlab-ci-scripts/goreleaser.sh index 15e36509..03728cb0 100755 --- a/.gitlab-ci-scripts/goreleaser.sh +++ b/.gitlab-ci-scripts/goreleaser.sh @@ -3,7 +3,7 @@ first=$(grep '^## ' -nm1 CHANGELOG.md | cut -d':' -f1); \ second=$(grep '^## ' -nm2 CHANGELOG.md | tail -n1 | cut -d':' -f1); \ tail -n+$first CHANGELOG.md | head -n$(($second-$first)) > ../shared/release.md GORELEASER_CONFIG=".goreleaser.yml" -if [ -z "$CI_COMMIT_TAG" ] || echo "$CI_COMMIT_TAG" | grep -qv; then +if ! [ -z "$CI_COMMIT_TAG" ] && echo "$CI_COMMIT_TAG" | grep -qv '-'; then GORELEASER_CONFIG=".goreleaser-release.yml" fi BASEDIR=/go/src/github.com/oidc-mytoken/server From fcf9ac81f18bd07c48801d7b1d559164d90cbb55 Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 08:56:52 +0100 Subject: [PATCH 50/55] fix unsafe defer of os.Close differently --- shared/utils/fileutil/fileutil.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shared/utils/fileutil/fileutil.go b/shared/utils/fileutil/fileutil.go index 799f5c82..6cff07f6 100644 --- a/shared/utils/fileutil/fileutil.go +++ b/shared/utils/fileutil/fileutil.go @@ -47,12 +47,11 @@ func Append(path, content string, doNotCreateIfDoesNotExist ...bool) error { if err != nil { return err } - defer f.Close() _, err = f.WriteString(content) if err != nil { return err } - return f.Sync() + return f.Close() } // ReadFile reads a given file and returns the content. From 256a267b1b6b618841bae4f3e11a07109bc79ba0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Nov 2022 03:05:52 +0000 Subject: [PATCH 51/55] Bump golang.org/x/crypto from 0.1.0 to 0.2.0 Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.1.0 to 0.2.0. - [Release notes](https://github.com/golang/crypto/releases) - [Commits](https://github.com/golang/crypto/compare/v0.1.0...v0.2.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index d68432d6..c7e31a83 100644 --- a/go.mod +++ b/go.mod @@ -26,10 +26,10 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/urfave/cli/v2 v2.3.1-0.20211205195634-e8d81738896c github.com/valyala/fasthttp v1.41.0 - golang.org/x/crypto v0.1.0 + golang.org/x/crypto v0.2.0 golang.org/x/mod v0.6.0 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 - golang.org/x/term v0.1.0 + golang.org/x/term v0.2.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -54,8 +54,8 @@ require ( github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/tcplisten v1.0.0 // indirect - golang.org/x/net v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect + golang.org/x/net v0.2.0 // indirect + golang.org/x/sys v0.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/protobuf v1.28.0 // indirect gopkg.in/square/go-jose.v2 v2.6.0 // indirect diff --git a/go.sum b/go.sum index e0e4dcee..ba568d36 100644 --- a/go.sum +++ b/go.sum @@ -483,8 +483,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.2.0 h1:BRXPfhNivWL5Yq0BGQ39a2sW6t44aODpfxkWjYdzewE= +golang.org/x/crypto v0.2.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -578,8 +578,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.0.0-20220906165146-f3363e06e74c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0= -golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -696,13 +696,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= -golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From b39fa2874f900aea5dbd5691b5ec1ef16805ee05 Mon Sep 17 00:00:00 2001 From: cicd Date: Fri, 11 Nov 2022 09:03:15 +0100 Subject: [PATCH 52/55] remove manual string trimming --- internal/model/version/version.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/internal/model/version/version.go b/internal/model/version/version.go index e6952655..cedb5911 100644 --- a/internal/model/version/version.go +++ b/internal/model/version/version.go @@ -29,10 +29,7 @@ func init() { ps := strings.Split(v[2], "-") FIX, _ = strconv.Atoi(ps[0]) if len(ps) > 1 { - pre := ps[1] - if strings.HasPrefix(pre, "pr") { - pre = pre[2:] - } + pre := strings.TrimPrefix(ps[1], "pr") PRE, _ = strconv.Atoi(pre) } } From 5516deb79284e5d1d221ceef05bba008d1f894e2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 10 Nov 2022 03:05:59 +0000 Subject: [PATCH 53/55] Bump golang.org/x/mod from 0.6.0 to 0.7.0 Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/golang/mod/releases) - [Commits](https://github.com/golang/mod/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c7e31a83..2b77b162 100644 --- a/go.mod +++ b/go.mod @@ -27,7 +27,7 @@ require ( github.com/urfave/cli/v2 v2.3.1-0.20211205195634-e8d81738896c github.com/valyala/fasthttp v1.41.0 golang.org/x/crypto v0.2.0 - golang.org/x/mod v0.6.0 + golang.org/x/mod v0.7.0 golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 golang.org/x/term v0.2.0 gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index ba568d36..47a0e349 100644 --- a/go.sum +++ b/go.sum @@ -522,8 +522,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0 h1:b9gGHsz9/HhJ3HF5DHQytPpuwocVTChQJK3AvoLRD5I= -golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI= +golang.org/x/mod v0.7.0 h1:LapD9S96VoQRhi/GrNTqeBJFrUjs5UHCAtTlgwA5oZA= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From b17bbd2ffaeb8f5a035c371761db4ddecd08317b Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 11 Nov 2022 11:12:54 +0100 Subject: [PATCH 54/55] [ci] on release tag push to preprod instead of prerel --- .gitlab-ci-scripts/goreleaser.sh | 2 +- .gitlab-ci-scripts/upload.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci-scripts/goreleaser.sh b/.gitlab-ci-scripts/goreleaser.sh index 03728cb0..fc057386 100755 --- a/.gitlab-ci-scripts/goreleaser.sh +++ b/.gitlab-ci-scripts/goreleaser.sh @@ -3,7 +3,7 @@ first=$(grep '^## ' -nm1 CHANGELOG.md | cut -d':' -f1); \ second=$(grep '^## ' -nm2 CHANGELOG.md | tail -n1 | cut -d':' -f1); \ tail -n+$first CHANGELOG.md | head -n$(($second-$first)) > ../shared/release.md GORELEASER_CONFIG=".goreleaser.yml" -if ! [ -z "$CI_COMMIT_TAG" ] && echo "$CI_COMMIT_TAG" | grep -qv '-'; then +if [ -n "$CI_COMMIT_TAG" ] && echo "$CI_COMMIT_TAG" | grep -qv '-'; then GORELEASER_CONFIG=".goreleaser-release.yml" fi BASEDIR=/go/src/github.com/oidc-mytoken/server diff --git a/.gitlab-ci-scripts/upload.sh b/.gitlab-ci-scripts/upload.sh index 40bca617..8eb567b1 100755 --- a/.gitlab-ci-scripts/upload.sh +++ b/.gitlab-ci-scripts/upload.sh @@ -1,6 +1,8 @@ -#REPO_TARGET="$(if echo "$CI_COMMIT_TAG" | grep -q '-'; then echo '/prerel'; else echo '/'; fi)" REPO_TARGET="/prerel" +if [ -n "$CI_COMMIT_TAG" ] && echo "$CI_COMMIT_TAG" | grep -qv '-'; then + REPO_TARGET="/preprod" +fi # ssh-key-script [ -e /tmp/ssh-private-keys/${REPO_USER} ] && { @@ -37,4 +39,3 @@ distribute_files() { upload_files distribute_files sign_repos -#echo "$CI_COMMIT_TAG" | grep -q '-' && sign_repos From c79ea9279eb7a33278e5fe4aeb68bcae381648f4 Mon Sep 17 00:00:00 2001 From: zachmann Date: Fri, 11 Nov 2022 11:32:08 +0100 Subject: [PATCH 55/55] [doc] update changelog --- CHANGELOG.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9602e20f..2409c907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,9 +38,11 @@ ### Dependencies - Bump go version to 1.19 -- Bump golang.org/x/mod from 0.5.1 to 0.6.0 -- Bump github.com/gofiber/fiber/v2 from 2.37.1 to 2.38.1 -- Bump github.com/gofiber/helmet/v2 from 2.2.16 to 2.2.17 +- Bump golang.org/x/mod from 0.5.1 to 0.7.0 +- Bump golang.org/x/crypto to 0.2.0 +- Bump golang.org/x/term to 0.2.0 +- Bump github.com/gofiber/fiber/v2 from 2.37.1 to 2.39.0 +- Bump github.com/gofiber/helmet/v2 from 2.2.16 to 2.2.18 ## mytoken 0.6.0