Skip to content

Commit

Permalink
Merge pull request #1099 from neicnordic/feature/auth-request-userinfo
Browse files Browse the repository at this point in the history
[auth] save userinfo to db
  • Loading branch information
MalinAhlberg authored Dec 2, 2024
2 parents 837b2c8 + 78a54bd commit 8b44fa2
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/integration/scripts/make_db_credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ set -e
apt-get -o DPkg::Lock::Timeout=60 update > /dev/null
apt-get -o DPkg::Lock::Timeout=60 install -y postgresql-client >/dev/null

for n in api download finalize inbox ingest mapper sync verify; do
for n in api auth download finalize inbox ingest mapper sync verify; do
echo "creating credentials for: $n"
psql -U postgres -h migrate -d sda -c "ALTER ROLE $n LOGIN PASSWORD '$n';"
psql -U postgres -h postgres -d sda -c "ALTER ROLE $n LOGIN PASSWORD '$n';"
Expand Down
2 changes: 1 addition & 1 deletion .github/integration/scripts/make_sda_credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ apt-get -o DPkg::Lock::Timeout=60 install -y curl jq openssh-client openssl post
pip install --upgrade pip > /dev/null
pip install aiohttp Authlib joserfc requests > /dev/null

for n in api download finalize inbox ingest mapper sync verify; do
for n in api auth download finalize inbox ingest mapper sync verify; do
echo "creating credentials for: $n"
psql -U postgres -h postgres -d sda -c "ALTER ROLE $n LOGIN PASSWORD '$n';"
psql -U postgres -h postgres -d sda -c "GRANT base TO $n;"
Expand Down
8 changes: 8 additions & 0 deletions .github/integration/sda-s3-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -302,10 +302,14 @@ services:
depends_on:
cega-nss:
condition: service_started
postgres:
condition: service_healthy
environment:
- AUTH_RESIGNJWT=true
- AUTH_CEGA_ID=test
- AUTH_CEGA_SECRET=test
- DB_PASSWORD=auth
- DB_USER=auth
image: ghcr.io/neicnordic/sensitive-data-archive:PR${PR_NUMBER}
ports:
- "8888:8080"
Expand All @@ -318,12 +322,16 @@ services:
command: [ sda-auth ]
container_name: auth-oidc
depends_on:
postgres:
condition: service_healthy
oidc:
condition: service_healthy
environment:
- AUTH_RESIGNJWT=false
- OIDC_ID=XC56EL11xx
- OIDC_SECRET=wHPVQaYXmdDHg
- DB_PASSWORD=auth
- DB_USER=auth
image: ghcr.io/neicnordic/sensitive-data-archive:PR${PR_NUMBER}
ports:
- "8889:8080"
Expand Down
2 changes: 2 additions & 0 deletions charts/sda-svc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ Parameter | Description | Default
`credentials.api.dbUser` | Database user for api | `""`
`credentials.api.dbPassword` | Database password for api | `""`
`credentials.api.mqUser` | Broker user for api | `""`
`credentials.auth.dbUser` | Database user for auth | `""`
`credentials.auth.dbPassword` | Database password for auth | `""`
`credentials.api.mqPassword` | Broker password for api | `""`
`credentials.doa.dbUser` | Database user for doa | `""`
`credentials.doa.dbPassword` | Database password for doa| `""`
Expand Down
8 changes: 8 additions & 0 deletions charts/sda-svc/templates/_helpers.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,14 @@ Create chart name and version as used by the chart label.
{{- ternary .Values.global.broker.password .Values.credentials.api.mqPassword (empty .Values.credentials.api.mqPassword) -}}
{{- end -}}

{{/**/}}
{{- define "dbUserAuth" -}}
{{- ternary .Values.global.db.user .Values.credentials.auth.dbUser (empty .Values.credentials.auth.dbUser) -}}
{{- end -}}
{{- define "dbPassAuth" -}}
{{- ternary .Values.global.db.password .Values.credentials.auth.dbPassword (empty .Values.credentials.auth.dbPassword) -}}
{{- end -}}

{{/**/}}
{{- define "dbUserSync" -}}
{{- ternary .Values.global.db.user .Values.credentials.sync.dbUser (empty .Values.credentials.sync.dbUser) -}}
Expand Down
1 change: 1 addition & 0 deletions charts/sda-svc/templates/auth-certificate.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ spec:
size: 256
usages:
- server auth
- client auth
# At least one of a DNS Name, URI, or IP address is required.
dnsNames:
- {{ template "sda.fullname" . }}-auth
Expand Down
31 changes: 31 additions & 0 deletions charts/sda-svc/templates/auth-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,37 @@ spec:
- name: SERVER_KEY
value: {{ template "tlsPath" . }}/tls.key
{{- end }}
{{- if .Values.global.tls.enabled }}
- name: DB_CACERT
value: {{ include "tlsPath" . }}/ca.crt
{{- if ne "verify-none" .Values.global.db.sslMode }}
- name: DB_CLIENTCERT
value: {{ include "tlsPath" . }}/tls.crt
- name: DB_CLIENTKEY
value: {{ include "tlsPath" . }}/tls.key
{{- end }}
- name: DB_SSLMODE
value: {{ .Values.global.db.sslMode | quote }}
{{- else }}
- name: DB_SSLMODE
value: "disable"
{{- end }}
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ template "sda.fullname" . }}-api
key: dbPassword
- name: DB_USER
valueFrom:
secretKeyRef:
name: {{ template "sda.fullname" . }}-api
key: dbUser
- name: DB_DATABASE
value: {{ default "lega" .Values.global.db.name | quote }}
- name: DB_HOST
value: {{ required "A valid DB host is required" .Values.global.db.host | quote }}
- name: DB_PORT
value: {{ .Values.global.db.port | quote }}
ports:
- name: auth
containerPort: 8080
Expand Down
2 changes: 2 additions & 0 deletions charts/sda-svc/templates/auth-secrets.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ data:
cegaID: {{ .Values.global.cega.user | quote | trimall "\"" | b64enc }}
cegaSecret: {{ .Values.global.cega.password | quote | trimall "\"" | b64enc }}
{{- end }}
dbPassword: {{ required "DB password is required" (include "dbPassAuth" .) | b64enc }}
dbUser: {{ required "DB user is required" (include "dbUserAuth" .) | b64enc }}
{{- end }}
{{- end }}
{{- end }}
4 changes: 4 additions & 0 deletions charts/sda-svc/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ credentials:
mqUser: ""
mqPassword: ""

auth:
dbUser: ""
dbPassword: ""

doa:
dbUser: ""
dbPassword: ""
Expand Down
11 changes: 10 additions & 1 deletion postgresql/initdb.d/01_main.sql
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ VALUES (0, now(), 'Created with version'),
(10, now(), 'Create Inbox user'),
(11, now(), 'Grant select permission to download on dataset_event_log'),
(12, now(), 'Add key hash'),
(13, now(), 'Create API user');
(13, now(), 'Create API user'),
(14, now(), 'Create Auth user');

-- Datasets are used to group files, and permissions are set on the dataset
-- level
Expand Down Expand Up @@ -74,6 +75,14 @@ CREATE TABLE files (
CONSTRAINT unique_ingested UNIQUE(submission_file_path, archive_file_path)
);

-- The user info is used by auth to be able to link users to their name and email
CREATE TABLE userinfo (
id TEXT PRIMARY KEY,
name TEXT,
email TEXT,
groups TEXT[]
);

-- To allow for multiple checksums per file, we use a dedicated table for it
CREATE TABLE checksums (
id SERIAL PRIMARY KEY,
Expand Down
7 changes: 6 additions & 1 deletion postgresql/initdb.d/04_grants.sql
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,15 @@ GRANT SELECT ON local_ega_ebi.file TO api;
GRANT SELECT ON local_ega_ebi.file_dataset TO api;

--------------------------------------------------------------------------------
CREATE ROLE auth;
GRANT USAGE ON SCHEMA sda TO auth;
GRANT SELECT, INSERT, UPDATE ON sda.userinfo TO auth;
--------------------------------------------------------------------------------

-- lega_in permissions
GRANT base, ingest, verify, finalize, sync, api TO lega_in;

-- lega_out permissions
GRANT mapper, download, api TO lega_out;

GRANT base TO api, download, inbox, ingest, finalize, mapper, verify
GRANT base TO api, download, inbox, ingest, finalize, mapper, verify, auth;
48 changes: 48 additions & 0 deletions postgresql/migratedb.d/14.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
DO
$$
DECLARE
-- The version we know how to do migration from, at the end of a successful migration
-- we will no longer be at this version.
sourcever INTEGER := 13;
changes VARCHAR := 'Add userinfo and create AUTH user';
BEGIN
IF (select max(version) from sda.dbschema_version) = sourcever then
RAISE NOTICE 'Doing migration from schema version % to %', sourcever, sourcever+1;
RAISE NOTICE 'Changes: %', changes;
INSERT INTO sda.dbschema_version VALUES(sourcever+1, now(), changes);

-- Temporary function for creating roles if they do not already exist.
CREATE FUNCTION create_role_if_not_exists(role_name NAME) RETURNS void AS $created$
BEGIN
IF EXISTS (
SELECT FROM pg_catalog.pg_roles
WHERE rolname = role_name) THEN
RAISE NOTICE 'Role "%" already exists. Skipping.', role_name;
ELSE
BEGIN
EXECUTE format('CREATE ROLE %I', role_name);
EXCEPTION
WHEN duplicate_object THEN
RAISE NOTICE 'Role "%" was just created by a concurrent transaction. Skipping.', role_name;
END;
END IF;
END;
$created$ LANGUAGE plpgsql;

CREATE TABLE IF NOT EXISTS sda.userinfo (
id TEXT PRIMARY KEY,
name TEXT,
email TEXT,
groups TEXT[]
);

PERFORM create_role_if_not_exists('auth');
GRANT USAGE ON SCHEMA sda TO auth;
GRANT SELECT, INSERT, UPDATE ON sda.userinfo TO auth;

GRANT base TO auth;
ELSE
RAISE NOTICE 'Schema migration from % to % does not apply now, skipping', sourcever, sourcever+1;
END IF;
END
$$
21 changes: 19 additions & 2 deletions sda/cmd/auth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/kataras/iris/v12/sessions"
"github.com/lestrrat-go/jwx/v2/jwt"
"github.com/neicnordic/sensitive-data-archive/internal/config"
"github.com/neicnordic/sensitive-data-archive/internal/database"
log "github.com/sirupsen/logrus"
"golang.org/x/oauth2"
)
Expand Down Expand Up @@ -263,13 +264,17 @@ func (auth AuthHandler) elixirLogin(ctx iris.Context) *OIDCData {

return nil
}
err = auth.Config.DB.UpdateUserInfo(idStruct.User, idStruct.Profile, idStruct.Email, idStruct.EdupersonEntitlement)
if err != nil {
log.Warn("Could not log user info.")
}

if auth.Config.ResignJwt {
claims := map[string]interface{}{
jwt.ExpirationKey: time.Now().UTC().Add(time.Duration(auth.Config.JwtTTL) * time.Hour),
jwt.IssuedAtKey: time.Now().UTC(),
jwt.IssuerKey: auth.Config.JwtIssuer,
jwt.SubjectKey: idStruct.User,
jwt.SubjectKey: idStruct.Profile,
}
token, expDate, err := generateJwtToken(claims, auth.Config.JwtPrivateKey, auth.Config.JwtSignatureAlg)
if err != nil {
Expand Down Expand Up @@ -397,6 +402,18 @@ func main() {

app.Use(sess.Handler())

// Connect to DB
authHandler.Config.DB, err = database.NewSDAdb(config.Database)
if err != nil {
log.Error(err)
panic(err)
}
if authHandler.Config.DB.Version < 14 {
log.Error("database schema v14 is required")
panic(err)
}
defer authHandler.Config.DB.Close()

app.RegisterView(iris.HTML(authHandler.htmlDir, ".html"))
app.HandleDir("/public", iris.Dir(authHandler.staticDir))

Expand All @@ -416,7 +433,7 @@ func main() {

authHandler.pubKey, err = readPublicKeyFile(authHandler.Config.PublicFile)
if err != nil {
log.Fatalf("Failed to read public key: %s", err.Error())
log.Panicf("Failed to read public key: %s", err.Error())
}

// Endpoint for client login info
Expand Down
10 changes: 10 additions & 0 deletions sda/internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ type OrchestratorConf struct {

type AuthConf struct {
OIDC OIDCConfig
DB *database.SDAdb
Cega CegaConfig
JwtIssuer string
JwtPrivateKey string
Expand Down Expand Up @@ -218,6 +219,11 @@ func NewConfig(app string) (*Config, error) {
requiredConfVars = []string{
"auth.s3Inbox",
"auth.publicFile",
"db.host",
"db.port",
"db.user",
"db.password",
"db.database",
}

if viper.GetString("auth.cega.id") != "" && viper.GetString("auth.cega.secret") != "" {
Expand Down Expand Up @@ -531,6 +537,10 @@ func NewConfig(app string) (*Config, error) {
}

c.Auth.S3Inbox = viper.GetString("auth.s3Inbox")
err := c.configDatabase()
if err != nil {
return nil, err
}
case "finalize":
if viper.GetString("archive.type") != "" && viper.GetString("backup.type") != "" {
c.configArchive()
Expand Down
3 changes: 0 additions & 3 deletions sda/internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ import (
"time"

log "github.com/sirupsen/logrus"

// Needed implicitly to enable Postgres driver
_ "github.com/lib/pq"
)

// DBConf stores information about how to connect to the database backend
Expand Down
33 changes: 33 additions & 0 deletions sda/internal/database/db_functions.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

"github.com/lib/pq"
log "github.com/sirupsen/logrus"
)

Expand Down Expand Up @@ -927,3 +928,35 @@ func (dbs *SDAdb) ListUserDatasets(submissionUser string) ([]DatasetInfo, error)

return datasets, nil
}

func (dbs *SDAdb) UpdateUserInfo(userID, name, email string, groups []string) error {
var (
err error
count int
)

for count == 0 || (err != nil && count < RetryTimes) {
err = dbs.updateUserInfo(userID, name, email, groups)
count++
}

return err
}
func (dbs *SDAdb) updateUserInfo(userID, name, email string, groups []string) error {
dbs.checkAndReconnectIfNeeded()

db := dbs.DB
const query = "INSERT INTO sda.userinfo(id, name, email, groups) VALUES($1, $2, $3, $4)" +
"ON CONFLICT (id)" +
"DO UPDATE SET name = excluded.name, email = excluded.email, groups = excluded.groups;"

result, err := db.Exec(query, userID, name, email, pq.Array(groups))
if err != nil {
return err
}
if rowsAffected, _ := result.RowsAffected(); rowsAffected == 0 {
return errors.New("something went wrong with the query zero rows were changed")
}

return nil
}
Loading

0 comments on commit 8b44fa2

Please sign in to comment.