Skip to content

Commit

Permalink
Merge pull request #1350 from kian99/merge-v3-feature-rebac-admin
Browse files Browse the repository at this point in the history
Merge v3 feature rebac admin
  • Loading branch information
kian99 authored Sep 4, 2024
2 parents f0c184b + 5d2faa0 commit abc1aed
Show file tree
Hide file tree
Showing 19 changed files with 141 additions and 314 deletions.
15 changes: 13 additions & 2 deletions .github/actions/test-server/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,21 @@ runs:
username: ${{ github.actor }}
password: ${{ inputs.ghcr-pat }}

# We can't use a make target here because a composite action
# doesn't have a .git folder when checked out.
- name: Start server based on released version
if: ${{ inputs.jimm-version != 'dev' }}
run: make integration-test-env
run: |
cd local/traefik/certs; ./certs.sh; cd - && \
docker compose --profile test up -d --wait
shell: bash
working-directory: ${{ github.action_path }}/../../..
env:
JIMM_VERSION: ${{ inputs.jimm-version }}

- name: Start server based on development version
if: ${{ inputs.jimm-version == 'dev' }}
working-directory: ${{ github.action_path }}/../../..
run: make dev-env
shell: bash

Expand All @@ -59,6 +65,7 @@ runs:
echo 'jimm-ca<<EOF' >> $GITHUB_OUTPUT
cat ./local/traefik/certs/ca.crt >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT
working-directory: ${{ github.action_path }}/../../..
shell: bash

- name: Initialise LXD
Expand All @@ -73,6 +80,7 @@ runs:
- name: Setup cloud-init script for bootstraping Juju controllers
run: ./local/jimm/setup-controller.sh
shell: bash
working-directory: ${{ github.action_path }}/../../..
env:
SKIP_BOOTSTRAP: true
CLOUDINIT_FILE: "cloudinit.temp.yaml"
Expand All @@ -83,7 +91,7 @@ runs:
provider: "lxd"
channel: "5.19/stable"
juju-channel: ${{ inputs.juju-channel }}
bootstrap-options: "--config cloudinit.temp.yaml --config login-token-refresh-url=https://jimm.localhost/.well-known/jwks.json"
bootstrap-options: "--config ${{ github.action_path }}/../../../cloudinit.temp.yaml --config login-token-refresh-url=https://jimm.localhost/.well-known/jwks.json"

# As described in https://github.com/charmed-kubernetes/actions-operator grab the newly setup controller name
- name: Save LXD controller name
Expand All @@ -100,18 +108,21 @@ runs:

- name: Authenticate Juju CLI
run: chmod -R 666 ~/.local/share/juju/*.yaml && ./local/jimm/setup-cli-auth.sh
working-directory: ${{ github.action_path }}/../../..
shell: bash
# Below is a hardcoded JWT using the same test-secret used in JIMM's docker compose and allows the CLI to authenticate as the [email protected] user.
env:
JWT: ZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKSVV6STFOaUo5LmV5SnBjM01pT2lKUGJteHBibVVnU2xkVUlFSjFhV3hrWlhJaUxDSnBZWFFpT2pFM01qUXlNamcyTmpBc0ltVjRjQ0k2TXprMk5EYzFNelEyTUN3aVlYVmtJam9pYW1sdGJTSXNJbk4xWWlJNkltcHBiVzB0ZEdWemRFQmpZVzV2Ym1sallXd3VZMjl0SW4wLkpTWVhXcGF6T0FnX1VFZ2hkbjlOZkVQdWxhWWlJQVdaX3BuSmRDbnJvWEk=

- name: Add LXD Juju controller to JIMM
run: ./local/jimm/add-controller.sh
working-directory: ${{ github.action_path }}/../../..
shell: bash
env:
JIMM_CONTROLLER_NAME: "jimm"
CONTROLLER_NAME: ${{ steps.lxd-controller.outputs.name }}

- name: Provide service account with cloud-credentials
run: ./local/jimm/setup-service-account.sh
working-directory: ${{ github.action_path }}/../../..
shell: bash
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ permissions:
jobs:
golangci:
name: Lint
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
10 changes: 9 additions & 1 deletion .github/workflows/release-server-rock.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,19 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Fix to build rock due to regression in LXD 5.21/stable https://discourse.ubuntu.com/t/mount-root-proc-cannot-mount-proc-read-only-with-lxd-5-21-2-22f93f4-from-snap/47533
- name: Setup LXD
uses: canonical/setup-lxd@main
with:
channel: 5.21/candidate

- name: ln rockcraft.yaml
run: ln -s ./rocks/jimm.yaml ./rockcraft.yaml

- name: Build ROCK
uses: canonical/craft-actions/rockcraft-pack@main
run: |
/usr/bin/sudo snap install --channel stable --classic rockcraft && \
rockcraft pack --verbosity trace --use-lxd
- name: Load ROCK into local registry
run: make load-rock
Expand Down
8 changes: 3 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ simplify:
gofmt -w -l -s .

# Generate version information
version/commit.txt: FORCE
version/commit.txt:
git rev-parse --verify HEAD > version/commit.txt

version/version.txt: FORCE
version/version.txt:
if [ -z "$(GIT_VERSION)" ]; then \
echo "dev" > version/version.txt; \
else \
Expand Down Expand Up @@ -155,6 +155,4 @@ help:
@echo 'make rock - Build the JIMM rock.'
@echo 'make load-rock - Load the most recently built rock into your local docker daemon.'

.PHONY: build check install release clean format server simplify sys-deps help FORCE

FORCE:
.PHONY: build check install release clean format server simplify sys-deps help
9 changes: 9 additions & 0 deletions charms/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
# JIMM Charms

Charms are a packaging tool for deploying application using Juju.
See more on charms [here](https://juju.is/charms-architecture).

JIMM has a machine charm and a Kubernetes charm for deploying the
application on different substrates. Currently the machine charm
is not maintained in favor of the K8s charm.

The machine charm has not yet been moved to a separate repo.

## K8S Charm
The K8S charm can be found [here](https://github.com/canonical/jimm-k8s-operator/).
19 changes: 11 additions & 8 deletions cmd/jimmsrv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ func start(ctx context.Context, s *service.Service) error {
return errors.E("jimm session store secret must be at least 64 characters")
}

corsAllowedOrigins := strings.Split(os.Getenv("CORS_ALLOWED_ORIGINS"), " ")

jimmsvc, err := jimmsvc.NewService(ctx, jimmsvc.Params{
ControllerUUID: os.Getenv("JIMM_UUID"),
DSN: os.Getenv("JIMM_DSN"),
Expand All @@ -167,17 +169,18 @@ func start(ctx context.Context, s *service.Service) error {
JWTExpiryDuration: jwtExpiryDuration,
InsecureSecretStorage: insecureSecretStorage,
OAuthAuthenticatorParams: jimmsvc.OAuthAuthenticatorParams{
IssuerURL: issuerURL,
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopesParsed,
SessionTokenExpiry: sessionTokenExpiryDuration,
SessionCookieMaxAge: sessionCookieMaxAgeInt,
JWTSessionKey: sessionSecretKey,
IssuerURL: issuerURL,
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopesParsed,
SessionTokenExpiry: sessionTokenExpiryDuration,
SessionCookieMaxAge: sessionCookieMaxAgeInt,
JWTSessionKey: sessionSecretKey,
SecureSessionCookies: secureSessionCookies,
},
DashboardFinalRedirectURL: os.Getenv("JIMM_DASHBOARD_FINAL_REDIRECT_URL"),
SecureSessionCookies: secureSessionCookies,
CookieSessionKey: []byte(sessionSecretKey),
CorsAllowedOrigins: corsAllowedOrigins,
})
if err != nil {
return err
Expand Down
23 changes: 18 additions & 5 deletions cmd/jimmsrv/service/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/juju/names/v5"
"github.com/juju/zaputil/zapctx"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/rs/cors"
"go.uber.org/zap"
"gorm.io/driver/postgres"
"gorm.io/gorm"
Expand Down Expand Up @@ -84,6 +85,10 @@ type OAuthAuthenticatorParams struct {
// SessionCookieMaxAge holds the max age for session cookies in seconds.
SessionCookieMaxAge int

// SecureSessionCookies determines if HTTPS must be enabled in order for JIMM
// to set cookies when creating browser based sessions.
SecureSessionCookies bool

// JWTSessionKey holds the secret key used for signing/verifying JWT tokens.
// See internal/auth/oauth2.go AuthenticationService.SessionSecretkey for more details.
JWTSessionKey string
Expand Down Expand Up @@ -177,14 +182,14 @@ type Params struct {
// the /callback in an authorisation code OAuth2.0 flow to finish the flow.
DashboardFinalRedirectURL string

// SecureSessionCookies determines if HTTPS must be enabled in order for JIMM
// to set cookies when creating browser based sessions.
SecureSessionCookies bool

// CookieSessionKey is a randomly generated secret passed via config used for signing
// cookie data. The recommended length is 32/64 characters from the Gorilla securecookie lib.
// https://github.com/gorilla/securecookie/blob/main/securecookie.go#L124
CookieSessionKey []byte

// CorsAllowedOrigins represents all addresses that are valid for cross-origin
// requests. A wildcard '*' is accepted to allow all cross-origin requests.
CorsAllowedOrigins []string
}

// A Service is the implementation of a JIMM server.
Expand Down Expand Up @@ -349,6 +354,7 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
SessionTokenExpiry: p.OAuthAuthenticatorParams.SessionTokenExpiry,
SessionCookieMaxAge: p.OAuthAuthenticatorParams.SessionCookieMaxAge,
JWTSessionKey: p.OAuthAuthenticatorParams.JWTSessionKey,
SecureCookies: p.OAuthAuthenticatorParams.SecureSessionCookies,
Store: &s.jimm.Database,
SessionStore: sessionStore,
RedirectURL: redirectUrl,
Expand Down Expand Up @@ -387,6 +393,14 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
return nil, errors.E(op, err)
}

// Setup CORS middleware
corsOpts := cors.New(cors.Options{
AllowedOrigins: p.CorsAllowedOrigins,
AllowedMethods: []string{"GET"},
AllowCredentials: true,
})
s.mux.Use(corsOpts.Handler)

// Setup all HTTP handlers.
mountHandler := func(path string, h jimmhttp.JIMMHttpHandler) {
s.mux.Mount(path, h.Routes())
Expand Down Expand Up @@ -415,7 +429,6 @@ func NewService(ctx context.Context, p Params) (*Service, error) {
oauthHandler, err := jimmhttp.NewOAuthHandler(jimmhttp.OAuthHandlerParams{
Authenticator: authSvc,
DashboardFinalRedirectURL: p.DashboardFinalRedirectURL,
SecureCookies: p.SecureSessionCookies,
})
if err != nil {
zapctx.Error(ctx, "failed to setup authentication handler", zap.Error(err))
Expand Down
44 changes: 44 additions & 0 deletions cmd/jimmsrv/service/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io"
"net/http"
"net/http/httptest"
"net/url"
"os"
"testing"

Expand Down Expand Up @@ -506,3 +507,46 @@ func TestCleanupDoesNotPanic_SessionStoreRelatedCleanups(t *testing.T) {

svc.Cleanup()
}

func TestCORS(t *testing.T) {
c := qt.New(t)

_, _, cofgaParams, err := jimmtest.SetupTestOFGAClient(c.Name())
c.Assert(err, qt.IsNil)
p := jimmtest.NewTestJimmParams(c)
p.OpenFGAParams = cofgaParamsToJIMMOpenFGAParams(*cofgaParams)
allowedOrigin := "http://my-referrer.com"
p.CorsAllowedOrigins = []string{allowedOrigin}
p.InsecureSecretStorage = true

svc, err := jimmsvc.NewService(context.Background(), p)
c.Assert(err, qt.IsNil)
defer svc.Cleanup()

srv := httptest.NewServer(svc)
c.Cleanup(srv.Close)

url, err := url.Parse(srv.URL + "/debug/info")
c.Assert(err, qt.IsNil)
// Invalid origin won't receive CORS headers.
req := http.Request{
Method: "GET",
URL: url,
Header: http.Header{"Origin": []string{"123"}},
}
response, err := srv.Client().Do(&req)
c.Assert(err, qt.IsNil)
defer response.Body.Close()
c.Assert(response.StatusCode, qt.Equals, http.StatusOK)
c.Assert(response.Header.Get("Access-Control-Allow-Credentials"), qt.Equals, "")
c.Assert(response.Header.Get("Access-Control-Allow-Origin"), qt.Equals, "")

// Valid origin should receive CORS headers.
req.Header = http.Header{"Origin": []string{allowedOrigin}}
response, err = srv.Client().Do(&req)
c.Assert(err, qt.IsNil)
defer response.Body.Close()
c.Assert(response.StatusCode, qt.Equals, http.StatusOK)
c.Assert(response.Header.Get("Access-Control-Allow-Credentials"), qt.Equals, "true")
c.Assert(response.Header.Get("Access-Control-Allow-Origin"), qt.Equals, allowedOrigin)
}
Loading

0 comments on commit abc1aed

Please sign in to comment.