Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api): valid email addresses for GPG keys #6687

Merged
merged 3 commits into from
Nov 17, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions engine/api/api.go
Original file line number Diff line number Diff line change
@@ -241,9 +241,10 @@ type Configuration struct {
CustomServiceJobBookDelay map[string]int64 `toml:"customServiceJobBookDelay" comment:"Set custom job book delay for given CDS Hatchery (in seconds)" json:"customServiceJobBookDelay" commented:"true"`
} `toml:"workflow" comment:"######################\n 'Workflow' global configuration \n######################" json:"workflow"`
Project struct {
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
VCSManagementDisabled bool `toml:"vcsManagementDisabled" comment:"Disable VCS management on project for CDS non admin users." json:"vcsManagementDisabled" default:"false" commented:"true"`
CreationDisabled bool `toml:"creationDisabled" comment:"Disable project creation for CDS non admin users." json:"creationDisabled" default:"false" commented:"true"`
InfoCreationDisabled string `toml:"infoCreationDisabled" comment:"Optional message to display if project creation is disabled." json:"infoCreationDisabled" default:"" commented:"true"`
VCSManagementDisabled bool `toml:"vcsManagementDisabled" comment:"Disable VCS management on project for CDS non admin users." json:"vcsManagementDisabled" default:"false" commented:"true"`
GPGKeyEmailAddressTemplate string `toml:"gpgKeyEmailAddressTemplate" comment:"Template for GPG Keys email address" json:"gpgKeyEmailAddressTemplate" default:"noreply+cds-{{.ProjectKey}}-{{.KeyName}}@localhost.local" commented:"true"`
} `toml:"project" comment:"######################\n 'Project' global configuration \n######################" json:"project"`
EventBus event.Config `toml:"events" comment:"######################\n Event bus configuration \n######################" json:"events" mapstructure:"events"`
VCS struct {
@@ -376,7 +377,7 @@ func (a *API) CheckConfiguration(config interface{}) error {

if ok, err := sdk.DirectoryExists(aConfig.Download.Directory); !ok {
if err := os.MkdirAll(aConfig.Download.Directory, os.FileMode(0700)); err != nil {
return fmt.Errorf("Unable to create directory %s: %v", aConfig.Download.Directory, err)
return fmt.Errorf("unable to create directory %s: %v", aConfig.Download.Directory, err)
}
log.Info(context.Background(), "Directory %s has been created", aConfig.Download.Directory)
} else if err != nil {
@@ -395,7 +396,7 @@ func (a *API) CheckConfiguration(config interface{}) error {
}
if ok, err := sdk.DirectoryExists(aConfig.Artifact.Local.BaseDirectory); !ok {
if err := os.MkdirAll(aConfig.Artifact.Local.BaseDirectory, os.FileMode(0700)); err != nil {
return fmt.Errorf("Unable to create directory %s: %v", aConfig.Artifact.Local.BaseDirectory, err)
return fmt.Errorf("unable to create directory %s: %v", aConfig.Artifact.Local.BaseDirectory, err)
}
log.Info(context.Background(), "Directory %s has been created", aConfig.Artifact.Local.BaseDirectory)
} else if err != nil {
@@ -411,7 +412,7 @@ func (a *API) CheckConfiguration(config interface{}) error {
}

if (aConfig.DefaultOS == "" && aConfig.DefaultArch != "") || (aConfig.DefaultOS != "" && aConfig.DefaultArch == "") {
return fmt.Errorf("You can't specify just defaultArch without defaultOS in your configuration and vice versa")
return fmt.Errorf("you can't specify just defaultArch without defaultOS in your configuration and vice versa")
}

if aConfig.Auth.RSAPrivateKey == "" && len(aConfig.Auth.RSAPrivateKeys) == 0 {
14 changes: 13 additions & 1 deletion engine/api/application.go
Original file line number Diff line number Diff line change
@@ -500,7 +500,19 @@ func (api *API) updateAsCodeApplicationHandler() service.Handler {
// create keys
for i := range a.Keys {
k := &a.Keys[i]
newKey, err := keys.GenerateKey(k.Name, k.Type)
var newKey sdk.Key
var err error
switch k.Type {
case sdk.KeyTypePGP:
var email string
email, err = api.gpgKeyEmailAddress(ctx, key, k.Name)
if err != nil {
return err
}
newKey, err = keys.GeneratePGPKeyPair(k.Name, "Application key generated by CDS", email)
case sdk.KeyTypeSSH:
newKey, err = keys.GenerateSSHKey(k.Name)
}
if err != nil {
return err
}
12 changes: 10 additions & 2 deletions engine/api/application/application_parser.go
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ type ImportOptions struct {
}

// ParseAndImport parse an exportentities.Application and insert or update the application in database
func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, cache cache.Store, proj sdk.Project, eapp *exportentities.Application, opts ImportOptions, decryptFunc keys.DecryptFunc, u sdk.Identifiable) (*sdk.Application, []sdk.Variable, []sdk.Message, error) {
func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, cache cache.Store, proj sdk.Project, eapp *exportentities.Application, opts ImportOptions, decryptFunc keys.DecryptFunc, u sdk.Identifiable, emailFunc keys.EmailFunc) (*sdk.Application, []sdk.Variable, []sdk.Message, error) {
log.Info(ctx, "ParseAndImport>> Import application %s in project %s (force=%v)", eapp.Name, proj.Key, opts.Force)
msgList := []sdk.Message{}

@@ -124,7 +124,15 @@ func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, cache
keepOldValue = true
}

kk, err := keys.Parse(ctx, db, proj.ID, kname, kval, decryptFunc)
var gpgEmail string
if kval.Type == sdk.KeyPGPParameter {
gpgEmail, err = emailFunc(ctx, proj.Key, kname)
if err != nil {
return app, nil, msgList, sdk.ErrorWithFallback(err, sdk.ErrWrongRequest, "unable to parse key %s", kname)
}
}

kk, err := keys.Parse(ctx, db, proj.ID, kname, kval, decryptFunc, gpgEmail)
if err != nil {
return app, nil, msgList, sdk.ErrorWithFallback(err, sdk.ErrWrongRequest, "unable to parse key %s", kname)
}
8 changes: 4 additions & 4 deletions engine/api/application/application_parser_test.go
Original file line number Diff line number Diff line change
@@ -40,15 +40,15 @@ name: ` + appName + `
require.NoError(t, errapp)

// try to import without force, it must give an error
_, _, _, globalError := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: false}, nil, u)
_, _, _, globalError := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: false}, nil, u, nil)
require.Error(t, globalError)

// try to import with force, but with another repository, it must give an error
_, _, _, globalError2 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true, FromRepository: "bar"}, nil, u)
_, _, _, globalError2 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true, FromRepository: "bar"}, nil, u, nil)
require.Error(t, globalError2)

// try to import with force, without a repo, it's ok
_, _, _, globalError3 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true}, nil, u)
_, _, _, globalError3 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true}, nil, u, nil)
require.NoError(t, globalError3)

}
@@ -96,7 +96,7 @@ name: ` + appName + `
assert.Equal(t, 1, len(events))

// try to import with force, without a repo, it's ok
_, _, _, globalError3 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true}, nil, u)
_, _, _, globalError3 := application.ParseAndImport(context.TODO(), db, cache, *proj, eapp, application.ImportOptions{Force: true}, nil, u, nil)
require.NoError(t, globalError3)

events, err = ascode.LoadEventsByWorkflowID(context.TODO(), db, wf.ID)
2 changes: 1 addition & 1 deletion engine/api/application_export_test.go
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ func Test_getApplicationExportHandler(t *testing.T) {
Type: sdk.KeyTypePGP,
ApplicationID: app.ID,
}
kk, err := keys.GeneratePGPKeyPair(k.Name)
kk, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
require.NoError(t, err)

k.Public = kk.Public
2 changes: 1 addition & 1 deletion engine/api/application_import.go
Original file line number Diff line number Diff line change
@@ -61,7 +61,7 @@ func (api *API) postApplicationImportHandler() service.Handler {
}
defer tx.Rollback() // nolint

newApp, _, msgList, globalError := application.ParseAndImport(ctx, tx, api.Cache, *proj, eapp, application.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getUserConsumer(ctx))
newApp, _, msgList, globalError := application.ParseAndImport(ctx, tx, api.Cache, *proj, eapp, application.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getUserConsumer(ctx), api.gpgKeyEmailAddress)
msgListString := translate(msgList)
if globalError != nil {
globalError = sdk.WrapError(globalError, "Unable to import application %s", eapp.Name)
6 changes: 3 additions & 3 deletions engine/api/application_import_test.go
Original file line number Diff line number Diff line change
@@ -111,7 +111,7 @@ func Test_postApplicationImportHandler_NewAppFromYAMLWithKeysAndSecrets(t *testi
ApplicationID: app.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
require.NoError(t, err)
k.Public = kpgp.Public
k.Private = kpgp.Private
@@ -219,7 +219,7 @@ func Test_postApplicationImportHandler_NewAppFromYAMLWithKeysAndSecretsAndReImpo
ApplicationID: app.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
require.NoError(t, err)
k.Public = kpgp.Public
k.Private = kpgp.Private
@@ -383,7 +383,7 @@ func Test_postApplicationImportHandler_NewAppFromYAMLWithKeysAndSecretsAndReImpo
ApplicationID: app.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k1.Name)
kpgp, err := keys.GeneratePGPKeyPair(k1.Name, "", "test@cds")
require.NoError(t, err)
k1.Public = kpgp.Public
k1.Private = kpgp.Private
13 changes: 12 additions & 1 deletion engine/api/application_key.go
Original file line number Diff line number Diff line change
@@ -104,7 +104,18 @@ func (api *API) addKeyInApplicationHandler() service.Handler {
newKey.Name = "app-" + newKey.Name
}

k, err := keys.GenerateKey(newKey.Name, newKey.Type)
var k sdk.Key
switch newKey.Type {
case sdk.KeyTypePGP:
var email string
email, err = api.gpgKeyEmailAddress(ctx, key, newKey.Name)
if err != nil {
return err
}
k, err = keys.GeneratePGPKeyPair(newKey.Name, "Application Key generated by CDS", email)
case sdk.KeyTypeSSH:
k, err = keys.GenerateSSHKey(newKey.Name)
}
if err != nil {
return err
}
2 changes: 1 addition & 1 deletion engine/api/application_key_test.go
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ func Test_getKeysInApplicationHandler(t *testing.T) {
ApplicationID: app.ID,
}

pgpK, err := keys.GeneratePGPKeyPair(k.Name)
pgpK, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
if err != nil {
t.Fatal(err)
}
2 changes: 1 addition & 1 deletion engine/api/ascode.go
Original file line number Diff line number Diff line change
@@ -193,7 +193,7 @@ func (api *API) postPerformImportAsCodeHandler() service.Handler {
if err != nil {
return err
}
msgPush, wrkflw, _, _, err := workflow.Push(ctx, api.mustDB(), api.Cache, proj, data, opt, getUserConsumer(ctx), project.DecryptWithBuiltinKey)
msgPush, wrkflw, _, _, err := workflow.Push(ctx, api.mustDB(), api.Cache, proj, data, opt, getUserConsumer(ctx), project.DecryptWithBuiltinKey, api.gpgKeyEmailAddress)
allMsg = append(allMsg, msgPush...)
if err != nil {
return sdk.WrapError(err, "unable to push workflow")
2 changes: 1 addition & 1 deletion engine/api/ascode_test.go
Original file line number Diff line number Diff line change
@@ -303,7 +303,7 @@ vcs_ssh_key: proj-blabla
`
var eapp = new(exportentities.Application)
assert.NoError(t, yaml.Unmarshal([]byte(appS), eapp))
app, _, _, globalError := application.ParseAndImport(context.Background(), db, api.Cache, *p, eapp, application.ImportOptions{Force: true}, nil, u)
app, _, _, globalError := application.ParseAndImport(context.Background(), db, api.Cache, *p, eapp, application.ImportOptions{Force: true}, nil, u, nil)
assert.NoError(t, globalError)

app.FromRepository = repoURL
14 changes: 13 additions & 1 deletion engine/api/environment.go
Original file line number Diff line number Diff line change
@@ -325,7 +325,19 @@ func (api *API) updateAsCodeEnvironmentHandler() service.Handler {
for i := range env.Keys {
k := &env.Keys[i]
if k.ID == 0 {
newKey, err := keys.GenerateKey(k.Name, k.Type)
var newKey sdk.Key
var err error
switch k.Type {
case sdk.KeyTypePGP:
var email string
email, err = api.gpgKeyEmailAddress(ctx, key, k.Name)
if err != nil {
return err
}
newKey, err = keys.GeneratePGPKeyPair(k.Name, "Environment Key generated by CDS", email) // TODO email address
case sdk.KeyTypeSSH:
newKey, err = keys.GenerateSSHKey(k.Name)
}
if err != nil {
return err
}
12 changes: 10 additions & 2 deletions engine/api/environment/environment_parser.go
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ type ImportOptions struct {
}

// ParseAndImport parse an exportentities.Environment and insert or update the environment in database
func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, proj sdk.Project, eenv exportentities.Environment, opts ImportOptions, decryptFunc keys.DecryptFunc, u sdk.Identifiable) (*sdk.Environment, []sdk.Variable, []sdk.Message, error) {
func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, proj sdk.Project, eenv exportentities.Environment, opts ImportOptions, decryptFunc keys.DecryptFunc, u sdk.Identifiable, emailFunc keys.EmailFunc) (*sdk.Environment, []sdk.Variable, []sdk.Message, error) {
log.Debug(ctx, "ParseAndImport>> Import environment %s in project %s from repository %q (force=%v)", eenv.Name, proj.Key, opts.FromRepository, opts.Force)
log.Debug(ctx, "ParseAndImport>> Env: %+v", eenv)

@@ -124,7 +124,15 @@ func ParseAndImport(ctx context.Context, db gorpmapper.SqlExecutorWithTx, proj s
keepOldValue = true
}

kk, err := keys.Parse(ctx, db, proj.ID, kname, kval, decryptFunc)
var gpgEmail string
if kval.Type == sdk.KeyPGPParameter {
gpgEmail, err = emailFunc(ctx, proj.Key, kname)
if err != nil {
return env, nil, nil, sdk.ErrorWithFallback(err, sdk.ErrWrongRequest, "unable to parse key %s", kname)
}
}

kk, err := keys.Parse(ctx, db, proj.ID, kname, kval, decryptFunc, gpgEmail)
if err != nil {
return env, nil, nil, sdk.WrapError(err, "Unable to parse key")
}
8 changes: 4 additions & 4 deletions engine/api/environment/environment_parser_test.go
Original file line number Diff line number Diff line change
@@ -41,13 +41,13 @@ name: ` + envName + `
errenv := yaml.Unmarshal(body, eenv)
require.NoError(t, errenv)

_, _, _, globalError := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: false}, nil, u)
_, _, _, globalError := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: false}, nil, u, nil)
require.Error(t, globalError)

_, _, _, globalError2 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true, FromRepository: "bar"}, nil, u)
_, _, _, globalError2 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true, FromRepository: "bar"}, nil, u, nil)
require.Error(t, globalError2)

_, _, _, globalError3 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true}, nil, u)
_, _, _, globalError3 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true}, nil, u, nil)
require.NoError(t, globalError3)
}
func TestParseAndImportCleanAsCode(t *testing.T) {
@@ -95,7 +95,7 @@ name: ` + envName + `
assert.Equal(t, 1, len(events))

// try to import with force, without a repo, it's ok
_, _, _, globalError3 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true}, nil, u)
_, _, _, globalError3 := environment.ParseAndImport(context.TODO(), db, *proj, *eenv, environment.ImportOptions{Force: true}, nil, u, nil)
require.NoError(t, globalError3)

events, err = ascode.LoadEventsByWorkflowID(context.TODO(), db, wf.ID)
2 changes: 1 addition & 1 deletion engine/api/environment_export_test.go
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ func Test_getEnvironmentExportHandler(t *testing.T) {
Type: sdk.KeyTypePGP,
EnvironmentID: env.ID,
}
kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
test.NoError(t, err)

k.Public = kpgp.Public
2 changes: 1 addition & 1 deletion engine/api/environment_import.go
Original file line number Diff line number Diff line change
@@ -58,7 +58,7 @@ func (api *API) postEnvironmentImportHandler() service.Handler {
}
defer tx.Rollback() // nolint

_, _, msgList, globalError := environment.ParseAndImport(ctx, tx, *proj, data, environment.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getUserConsumer(ctx))
_, _, msgList, globalError := environment.ParseAndImport(ctx, tx, *proj, data, environment.ImportOptions{Force: force}, project.DecryptWithBuiltinKey, getUserConsumer(ctx), api.gpgKeyEmailAddress)
msgListString := translate(msgList)
if globalError != nil {
globalError = sdk.WrapError(globalError, "Unable to import environment %s", data.Name)
4 changes: 2 additions & 2 deletions engine/api/environment_import_test.go
Original file line number Diff line number Diff line change
@@ -105,7 +105,7 @@ func Test_postEnvironmentImportHandler_NewEnvFromYAMLWithKeysAndSecrets(t *testi
EnvironmentID: env.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
require.NoError(t, err)
k.Public = kpgp.Public
k.Private = kpgp.Private
@@ -222,7 +222,7 @@ func Test_postEnvironmentImportHandler_NewEnvFromYAMLWithKeysAndSecretsAndReImpo
EnvironmentID: env.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
require.NoError(t, err)
k.Public = kpgp.Public
k.Private = kpgp.Private
15 changes: 14 additions & 1 deletion engine/api/environment_key.go
Original file line number Diff line number Diff line change
@@ -102,10 +102,23 @@ func (api *API) addKeyInEnvironmentHandler() service.Handler {
newKey.Name = "env-" + newKey.Name
}

k, err := keys.GenerateKey(newKey.Name, newKey.Type)
var k sdk.Key
var err error
switch newKey.Type {
case sdk.KeyTypePGP:
var email string
email, err = api.gpgKeyEmailAddress(ctx, key, newKey.Name)
if err != nil {
return err
}
k, err = keys.GeneratePGPKeyPair(newKey.Name, "Environment Key generated by CDS", email) // TODO email address
case sdk.KeyTypeSSH:
k, err = keys.GenerateSSHKey(newKey.Name)
}
if err != nil {
return err
}

newKey.Public = k.Public
newKey.Private = k.Private
newKey.ID = k.ID
2 changes: 1 addition & 1 deletion engine/api/environment_key_test.go
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ func Test_getKeysInEnvironmentHandler(t *testing.T) {
EnvironmentID: env.ID,
}

kpgp, err := keys.GeneratePGPKeyPair(k.Name)
kpgp, err := keys.GeneratePGPKeyPair(k.Name, "", "test@cds")
if err != nil {
t.Fatal(err)
}
14 changes: 0 additions & 14 deletions engine/api/keys/key.go

This file was deleted.

Loading