├── .blubracket └── ignore.yaml ├── .dockerignore ├── .github ├── Contributing.md ├── dependabot.yml ├── semantic.yml └── workflows │ ├── codeql-analysis.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── .hadolint.yml ├── .sonarcloud.properties ├── ADOPTERS.md ├── Attribution.txt ├── CHANGELOG.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── Jenkinsfile ├── LICENSE ├── Makefile ├── OWNERS.md ├── README.md ├── SECURITY.md ├── bin └── test-attribution-txt.sh ├── cmd ├── core-command │ ├── Dockerfile │ ├── main.go │ └── res │ │ └── configuration.yaml ├── core-common-config-bootstrapper │ ├── Dockerfile │ ├── entrypoint.sh │ ├── main.go │ └── res │ │ └── configuration.yaml ├── core-data │ ├── Dockerfile │ ├── main.go │ └── res │ │ └── configuration.yaml ├── core-keeper │ ├── Dockerfile │ ├── main.go │ └── res │ │ └── configuration.yaml ├── core-metadata │ ├── Dockerfile │ ├── main.go │ └── res │ │ ├── configuration.yaml │ │ └── uom.yaml ├── secrets-config │ ├── README.md │ ├── main.go │ └── res │ │ └── configuration.yaml ├── security-bootstrapper │ ├── Dockerfile │ ├── entrypoint-scripts │ │ ├── kuiper_wait_install.sh │ │ ├── messagebus_wait_install.sh │ │ ├── nginx_wait_install.sh │ │ ├── postgres_wait_install.sh │ │ ├── proxy_setup_wait_install.sh │ │ ├── ready_to_run_wait_install.sh │ │ ├── redis_wait_install.sh │ │ └── secretstore_wait_install.sh │ ├── entrypoint.sh │ ├── main.go │ ├── res-bootstrap-mosquitto │ │ └── configuration.yaml │ ├── res-bootstrap-postgres │ │ └── configuration.yaml │ ├── res-bootstrap-redis │ │ └── configuration.yaml │ └── res │ │ └── configuration.yaml ├── security-file-token-provider │ ├── README.md │ ├── main.go │ └── res │ │ └── token-config.json ├── security-proxy-auth │ ├── Dockerfile │ ├── entrypoint.sh │ ├── main.go │ └── res │ │ └── configuration.yaml ├── security-proxy-setup │ ├── Dockerfile │ ├── README.md │ └── entrypoint.sh ├── security-secretstore-setup │ ├── Dockerfile │ ├── README.md │ ├── entrypoint.sh │ ├── main.go │ ├── res-file-token-provider │ │ └── configuration.yaml │ └── res │ │ └── configuration.yaml ├── security-spiffe-token-provider │ ├── Dockerfile │ ├── README.md │ ├── main.go │ └── res │ │ └── configuration.yaml ├── security-spire-agent │ ├── Dockerfile │ ├── agent.conf │ ├── docker-entrypoint.sh │ └── openssl.conf ├── security-spire-config │ ├── Dockerfile │ ├── docker-entrypoint.sh │ └── seed_builtin_entries.sh ├── security-spire-server │ ├── Dockerfile │ ├── docker-entrypoint.sh │ ├── openssl.conf │ └── server.conf ├── support-notifications │ ├── Dockerfile │ ├── main.go │ └── res │ │ └── configuration.yaml └── support-scheduler │ ├── Dockerfile │ ├── main.go │ └── res │ └── configuration.yaml ├── fuzz_test ├── Dockerfile.fuzz └── fuzzing_docker.sh ├── go.mod ├── go.sum ├── internal ├── constants.go ├── core │ ├── command │ │ ├── README.md │ │ ├── application │ │ │ ├── command.go │ │ │ └── command_test.go │ │ ├── config │ │ │ └── config.go │ │ ├── container │ │ │ └── config.go │ │ ├── controller │ │ │ ├── http │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ └── messaging │ │ │ │ ├── external.go │ │ │ │ ├── external_test.go │ │ │ │ ├── internal.go │ │ │ │ ├── internal_test.go │ │ │ │ ├── mocks │ │ │ │ ├── Client.go │ │ │ │ ├── Message.go │ │ │ │ └── Token.go │ │ │ │ └── utils.go │ │ ├── init.go │ │ ├── main.go │ │ └── router.go │ ├── common_config │ │ ├── README.md │ │ ├── main.go │ │ ├── utils.go │ │ └── utils_test.go │ ├── data │ │ ├── README.md │ │ ├── application │ │ │ ├── app.go │ │ │ ├── event.go │ │ │ ├── event_test.go │ │ │ ├── reading.go │ │ │ └── reading_test.go │ │ ├── config │ │ │ └── config.go │ │ ├── container │ │ │ ├── config.go │ │ │ ├── database.go │ │ │ └── device.go │ │ ├── controller │ │ │ ├── http │ │ │ │ ├── const.go │ │ │ │ ├── const_test.go │ │ │ │ ├── event.go │ │ │ │ ├── event_test.go │ │ │ │ ├── reading.go │ │ │ │ └── reading_test.go │ │ │ └── messaging │ │ │ │ ├── subscriber.go │ │ │ │ └── system_event.go │ │ ├── embed │ │ │ ├── consts.go │ │ │ ├── schema.go │ │ │ └── sql │ │ │ │ ├── idempotent │ │ │ │ ├── 00-utils.sql │ │ │ │ └── 01-tables.sql │ │ │ │ └── versions │ │ │ │ └── 4.0.0-dev │ │ │ │ └── 00-placeholder.sql │ │ ├── infrastructure │ │ │ └── interfaces │ │ │ │ ├── db.go │ │ │ │ └── mocks │ │ │ │ └── DBClient.go │ │ ├── init.go │ │ ├── main.go │ │ ├── mocks │ │ │ └── container.go │ │ └── router.go │ ├── keeper │ │ ├── README.md │ │ ├── application │ │ │ ├── kv.go │ │ │ └── registry.go │ │ ├── config │ │ │ └── config.go │ │ ├── constants │ │ │ └── const.go │ │ ├── container │ │ │ ├── config.go │ │ │ ├── database.go │ │ │ └── registry.go │ │ ├── controller │ │ │ └── http │ │ │ │ ├── kv.go │ │ │ │ ├── kv_test.go │ │ │ │ ├── registry.go │ │ │ │ └── registry_test.go │ │ ├── embed │ │ │ ├── consts.go │ │ │ ├── schema.go │ │ │ └── sql │ │ │ │ ├── idempotent │ │ │ │ ├── 00-utils.sql │ │ │ │ └── 01-tables.sql │ │ │ │ └── versions │ │ │ │ └── 4.0.0-dev │ │ │ │ └── 00-placeholder.sql │ │ ├── infrastructure │ │ │ └── interfaces │ │ │ │ ├── db.go │ │ │ │ ├── mocks │ │ │ │ ├── DBClient.go │ │ │ │ └── Registry.go │ │ │ │ └── registry.go │ │ ├── init.go │ │ ├── main.go │ │ ├── registry │ │ │ ├── bootstrap.go │ │ │ └── registry.go │ │ ├── router.go │ │ └── utils │ │ │ ├── http.go │ │ │ └── key.go │ └── metadata │ │ ├── README.md │ │ ├── application │ │ ├── capacity.go │ │ ├── device.go │ │ ├── device_test.go │ │ ├── devicecommand.go │ │ ├── deviceprofile.go │ │ ├── deviceresource.go │ │ ├── deviceservice.go │ │ ├── notify.go │ │ ├── notify_test.go │ │ └── provisionwatcher.go │ │ ├── config │ │ └── config.go │ │ ├── const.go │ │ ├── container │ │ ├── config.go │ │ ├── database.go │ │ ├── lock.go │ │ └── uom.go │ │ ├── controller │ │ └── http │ │ │ ├── const_test.go │ │ │ ├── device.go │ │ │ ├── device_test.go │ │ │ ├── devicecommand.go │ │ │ ├── devicecommand_test.go │ │ │ ├── deviceprofile.go │ │ │ ├── deviceprofile_test.go │ │ │ ├── deviceresource.go │ │ │ ├── deviceresource_test.go │ │ │ ├── deviceservice.go │ │ │ ├── deviceservice_test.go │ │ │ ├── provisionwatcher.go │ │ │ ├── provisionwatcher_test.go │ │ │ ├── uom.go │ │ │ └── uom_test.go │ │ ├── embed │ │ ├── consts.go │ │ ├── schema.go │ │ └── sql │ │ │ ├── idempotent │ │ │ ├── 00-utils.sql │ │ │ └── 01-tables.sql │ │ │ └── versions │ │ │ └── 4.0.0-dev │ │ │ └── 00-placeholder.sql │ │ ├── infrastructure │ │ └── interfaces │ │ │ ├── db.go │ │ │ ├── mocks │ │ │ ├── DBClient.go │ │ │ └── UnitsOfMeasure.go │ │ │ └── uom.go │ │ ├── init.go │ │ ├── main.go │ │ ├── router.go │ │ ├── uom │ │ ├── bootstrap.go │ │ └── uom.go │ │ └── utils │ │ └── lock.go ├── interface.go ├── io │ ├── reader.go │ └── reader_test.go ├── pkg │ ├── bootstrap │ │ ├── handlers │ │ │ └── database.go │ │ └── interfaces │ │ │ └── database.go │ ├── cache │ │ └── device.go │ ├── common │ │ └── util.go │ ├── correlation │ │ └── handler.go │ ├── db │ │ ├── db.go │ │ ├── postgres │ │ │ ├── client.go │ │ │ ├── lock.go │ │ │ ├── migration.go │ │ │ ├── sql.go │ │ │ └── utils.go │ │ └── redis │ │ │ └── client.go │ ├── encoding.go │ ├── infrastructure │ │ ├── postgres │ │ │ ├── cache │ │ │ │ └── deviceinfo.go │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── consts.go │ │ │ ├── device.go │ │ │ ├── device_profile.go │ │ │ ├── device_service.go │ │ │ ├── deviceinfo.go │ │ │ ├── event.go │ │ │ ├── keystore.go │ │ │ ├── kv.go │ │ │ ├── models │ │ │ │ ├── deviceinfo.go │ │ │ │ └── reading.go │ │ │ ├── notification.go │ │ │ ├── provision_watcher.go │ │ │ ├── reading.go │ │ │ ├── registry.go │ │ │ ├── scheduleactionrecord.go │ │ │ ├── schedulejob.go │ │ │ ├── sql.go │ │ │ ├── subscription.go │ │ │ ├── transmission.go │ │ │ └── utils.go │ │ └── redis │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── dbconsts.go │ │ │ ├── dbhelper.go │ │ │ ├── dbhelper_test.go │ │ │ ├── device.go │ │ │ ├── device_profile.go │ │ │ ├── device_service.go │ │ │ ├── event.go │ │ │ ├── kv.go │ │ │ ├── notification.go │ │ │ ├── provisionwatcher.go │ │ │ ├── queries.go │ │ │ ├── reading.go │ │ │ ├── reading_test.go │ │ │ ├── registry.go │ │ │ ├── subscription.go │ │ │ └── transmission.go │ ├── interfaces │ │ └── db.go │ └── utils │ │ ├── count.go │ │ ├── count_test.go │ │ ├── crypto │ │ ├── aes.go │ │ ├── aes_test.go │ │ └── interfaces │ │ │ ├── crypto.go │ │ │ └── mocks │ │ │ └── Crypto.go │ │ ├── event.go │ │ ├── http.go │ │ ├── http_test.go │ │ ├── restaddress.go │ │ ├── struct.go │ │ ├── struct_test.go │ │ ├── time.go │ │ └── time_test.go ├── security │ ├── bootstrapper │ │ ├── README.md │ │ ├── command │ │ │ ├── cmd_dispatcher.go │ │ │ ├── cmd_dispatcher_test.go │ │ │ ├── flags_common.go │ │ │ ├── gate │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ ├── genpassword │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ ├── gethttpstatus │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ ├── help │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ ├── listen │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ └── waitfor │ │ │ │ ├── command.go │ │ │ │ ├── command_test.go │ │ │ │ └── uri_flags.go │ │ ├── config │ │ │ ├── config.go │ │ │ ├── types.go │ │ │ └── waitFor.go │ │ ├── container │ │ │ └── container.go │ │ ├── handlers │ │ │ └── init.go │ │ ├── helper │ │ │ ├── helper.go │ │ │ ├── helper_test.go │ │ │ ├── postgres_script.go │ │ │ ├── postgres_script_test.go │ │ │ ├── redis_conf.go │ │ │ └── redis_config_test.go │ │ ├── interfaces │ │ │ └── command.go │ │ ├── main.go │ │ ├── messagebus_factory.go │ │ ├── mosquitto │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── configure.go │ │ │ ├── container │ │ │ │ └── config.go │ │ │ └── handlers │ │ │ │ ├── handlers.go │ │ │ │ └── handlers_test.go │ │ ├── postgres │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── configure.go │ │ │ ├── container │ │ │ │ └── config.go │ │ │ └── handlers │ │ │ │ ├── handler_test.go │ │ │ │ └── handlers.go │ │ ├── redis │ │ │ ├── Developer-notes.md │ │ │ ├── config │ │ │ │ └── config.go │ │ │ ├── configure.go │ │ │ ├── container │ │ │ │ └── config.go │ │ │ └── handlers │ │ │ │ └── handlers.go │ │ └── tcp │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── listener.go │ │ │ └── listener_test.go │ ├── common │ │ ├── tokenpolicy.go │ │ ├── tokenpolicy_test.go │ │ ├── usermanager.go │ │ └── usermanager_test.go │ ├── config │ │ ├── bootstraphandler.go │ │ ├── command │ │ │ ├── flags_common.go │ │ │ ├── help │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ └── proxy │ │ │ │ ├── adduser │ │ │ │ ├── command.go │ │ │ │ ├── command_test.go │ │ │ │ └── testdata │ │ │ │ │ └── token.json │ │ │ │ ├── command.go │ │ │ │ ├── deluser │ │ │ │ ├── command.go │ │ │ │ ├── command_test.go │ │ │ │ └── testdata │ │ │ │ │ └── token.json │ │ │ │ ├── shared │ │ │ │ └── proxyuser.go │ │ │ │ └── tls │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ ├── interfaces │ │ │ ├── command.go │ │ │ └── constants.go │ │ └── main.go │ ├── fileprovider │ │ ├── command │ │ │ ├── createtoken │ │ │ │ ├── command.go │ │ │ │ └── command_test.go │ │ │ ├── flags_common.go │ │ │ └── handlers │ │ │ │ └── init.go │ │ ├── config │ │ │ └── config.go │ │ ├── container │ │ │ └── config.go │ │ ├── init.go │ │ ├── main.go │ │ ├── mocks │ │ │ ├── mock.go │ │ │ └── mock_test.go │ │ ├── res │ │ │ └── edgex-privileged-token-creator.hcl │ │ ├── tokenprovider │ │ │ ├── interface.go │ │ │ ├── provider.go │ │ │ ├── provider_test.go │ │ │ ├── tokenconfig.go │ │ │ └── tokenconfig_test.go │ │ ├── types.go │ │ ├── util.go │ │ └── util_test.go │ ├── kdf │ │ ├── interface.go │ │ ├── methods.go │ │ ├── methods_test.go │ │ └── mocks │ │ │ ├── mock.go │ │ │ └── mock_test.go │ ├── pipedhexreader │ │ ├── interface.go │ │ ├── mocks │ │ │ ├── mock.go │ │ │ └── mock_test.go │ │ ├── pipedhexreader.go │ │ ├── pipedhexreader_linux_test.go │ │ └── testdata │ │ │ ├── echowithnewline │ │ │ └── echowithoutnewline │ ├── proxyauth │ │ ├── application │ │ │ ├── key.go │ │ │ └── key_test.go │ │ ├── config │ │ │ └── config.go │ │ ├── container │ │ │ ├── config.go │ │ │ ├── cryptor.go │ │ │ └── database.go │ │ ├── controller │ │ │ ├── controller.go │ │ │ ├── controller_test.go │ │ │ ├── key.go │ │ │ └── key_test.go │ │ ├── embed │ │ │ ├── consts.go │ │ │ ├── schema.go │ │ │ └── sql │ │ │ │ ├── idempotent │ │ │ │ ├── 00-utils.sql │ │ │ │ └── 01-tables.sql │ │ │ │ └── versions │ │ │ │ └── 4.0.0-dev │ │ │ │ └── 00-placeholder.sql │ │ ├── infrastructure │ │ │ └── interfaces │ │ │ │ ├── db.go │ │ │ │ └── mocks │ │ │ │ └── DBClient.go │ │ ├── init.go │ │ ├── main.go │ │ ├── router.go │ │ └── utils │ │ │ ├── key.go │ │ │ └── key_test.go │ ├── secretstore │ │ ├── certs.go │ │ ├── certs_test.go │ │ ├── config │ │ │ └── config.go │ │ ├── container │ │ │ └── config.go │ │ ├── controller │ │ │ └── token.go │ │ ├── credentialgenerator.go │ │ ├── credentialgenerator_test.go │ │ ├── init.go │ │ ├── init_test.go │ │ ├── main.go │ │ ├── password.go │ │ ├── password_linux_test.go │ │ ├── password_test.go │ │ ├── passwordprovider.go │ │ ├── passwordprovider_test.go │ │ ├── secretsengine │ │ │ ├── enabler.go │ │ │ └── enabler_test.go │ │ ├── secure-messagebus.go │ │ ├── secure-messagebus_test.go │ │ ├── server │ │ │ ├── configure.go │ │ │ └── handlers │ │ │ │ ├── initserver.go │ │ │ │ └── router.go │ │ ├── testdata │ │ │ └── test-resp-init.json │ │ ├── tokencreatable │ │ │ └── tokencreatable.go │ │ ├── tokenfilewriter │ │ │ ├── tokenfilewriter.go │ │ │ └── tokenfilewriter_test.go │ │ ├── tokeninit │ │ │ └── secretstore.go │ │ ├── tokenmaintenance │ │ │ ├── constants.go │ │ │ ├── initresp.go │ │ │ ├── initresp_test.go │ │ │ ├── tokenmaintenance.go │ │ │ └── tokenmaintenance_test.go │ │ ├── tokenprovider │ │ │ ├── tokenprovider.go │ │ │ ├── tokenprovider_linux_test.go │ │ │ └── tokenprovider_test.go │ │ ├── utils │ │ │ ├── execrunner-mock.go │ │ │ └── execrunner.go │ │ ├── vmkencryption.go │ │ └── vmkencryption_test.go │ └── spiffetokenprovider │ │ ├── config │ │ └── config.go │ │ ├── container │ │ └── config.go │ │ ├── init.go │ │ ├── main.go │ │ └── maketoken.go └── support │ ├── notifications │ ├── application │ │ ├── channel │ │ │ ├── container.go │ │ │ ├── email.go │ │ │ ├── mocks │ │ │ │ └── Sender.go │ │ │ ├── mqtt.go │ │ │ ├── sender.go │ │ │ └── zmq.go │ │ ├── distributioncoordinator.go │ │ ├── notification.go │ │ ├── notification_test.go │ │ ├── send.go │ │ ├── send_test.go │ │ ├── subscription.go │ │ ├── subscription_test.go │ │ └── transmission.go │ ├── config │ │ └── config.go │ ├── container │ │ ├── config.go │ │ └── database.go │ ├── controller │ │ └── http │ │ │ ├── notification.go │ │ │ ├── notification_test.go │ │ │ ├── subscription.go │ │ │ ├── subscription_test.go │ │ │ ├── transmission.go │ │ │ └── transmission_test.go │ ├── embed │ │ ├── consts.go │ │ ├── schema.go │ │ └── sql │ │ │ ├── idempotent │ │ │ ├── 00-utils.sql │ │ │ └── 01-tables.sql │ │ │ └── versions │ │ │ └── 4.0.0-dev │ │ │ └── 00-placeholder.sql │ ├── infrastructure │ │ └── interfaces │ │ │ ├── db.go │ │ │ └── mocks │ │ │ └── DBClient.go │ ├── init.go │ ├── main.go │ └── router.go │ └── scheduler │ ├── application │ ├── action │ │ ├── devicecontrol.go │ │ ├── edgexmessagebus.go │ │ ├── gocron.go │ │ └── rest.go │ ├── scheduleactionrecord.go │ ├── scheduleactionrecord_test.go │ └── schedulejob.go │ ├── config │ └── config.go │ ├── container │ ├── config.go │ ├── database.go │ └── scheduler.go │ ├── controller │ └── http │ │ ├── scheduleactionrecord.go │ │ ├── scheduleactionrecord_test.go │ │ ├── schedulejob.go │ │ ├── schedulejob_test.go │ │ └── utils_test.go │ ├── embed │ ├── consts.go │ ├── schema.go │ └── sql │ │ ├── idempotent │ │ ├── 00-utils.sql │ │ └── 01-tables.sql │ │ └── versions │ │ └── 4.0.0-dev │ │ └── 00-placeholder.sql │ ├── infrastructure │ ├── interfaces │ │ ├── SchedulerManager.go │ │ ├── db.go │ │ └── mocks │ │ │ ├── DBClient.go │ │ │ └── SchedulerManager.go │ ├── manager.go │ └── manager_test.go │ ├── init.go │ ├── main.go │ └── router.go ├── openapi ├── README.md ├── core-command.yaml ├── core-data.yaml ├── core-keeper.yaml ├── core-metadata.yaml ├── security-proxy-auth.yaml ├── security-secretstore-setup.yaml ├── support-notifications.yaml └── support-scheduler.yaml ├── security.txt └── version.go /.blubracket/ignore.yaml: -------------------------------------------------------------------------------- 1 | - paths: 2 | - Attribution.txt 3 | - "**/*_test.go" 4 | - "vendor/**" 5 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | docs 2 | .git 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # Maintain dependencies for Go modules 4 | - package-ecosystem: "gomod" 5 | directory: "/" 6 | schedule: 7 | interval: "daily" 8 | ignore: 9 | - dependency-name: "github.com/edgexfoundry/go-mod-configuration/v4" 10 | # Included when go-mod-bootstrap is updated 11 | - dependency-name: "github.com/edgexfoundry/go-mod-core-contracts/v4" 12 | # Included when go-mod-bootstrap is updated 13 | - dependency-name: "github.com/edgexfoundry/go-mod-messaging/v4" 14 | # Included when go-mod-bootstrap is updated 15 | - dependency-name: "github.com/edgexfoundry/go-mod-registry/v4" 16 | # Included when go-mod-bootstrap is updated 17 | - dependency-name: "github.com/edgexfoundry/go-mod-secrets/v4" 18 | # Included when go-mod-bootstrap is updated 19 | - dependency-name: "github.com/gomodule/redigo" 20 | # For github.com/gomodule/redigo, ignore version v2.0.0 21 | versions: ["v2.0.0"] 22 | -------------------------------------------------------------------------------- /.github/semantic.yml: -------------------------------------------------------------------------------- 1 | allowMergeCommits: true 2 | # Always validate the PR title AND all the commits 3 | titleAndCommits: true 4 | types: 5 | - feat 6 | - fix 7 | - docs 8 | - style 9 | - refactor 10 | - perf 11 | - test 12 | - build 13 | - ci 14 | - revert 15 | scopes: 16 | - core-data 17 | - data 18 | - core-metadata 19 | - metadata 20 | - meta 21 | - core-command 22 | - command 23 | - cmd 24 | - docker 25 | - security 26 | - scheduler 27 | - notifications 28 | - sma 29 | - deps 30 | - all 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | on: 3 | push: 4 | tags: 5 | - 'v*.*.*' 6 | - '!v*-dev*' 7 | jobs: 8 | release: 9 | uses: edgexfoundry/edgex-global-pipelines/.github/workflows/release.yml@stable 10 | permissions: 11 | contents: write -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | vendor 3 | *.exe 4 | glide.lock 5 | *.DS_Store 6 | docker/docker-compose.yml 7 | .project 8 | .vscode/ 9 | coverage.out 10 | /debug/ 11 | *.dll 12 | VERSION 13 | __debug_bin 14 | sbom/bom-go-mod.spdx 15 | 16 | # binary files 17 | cmd/core-command/core-command 18 | cmd/core-common-config-bootstrapper/core-common-config-bootstrapper 19 | cmd/core-data/core-data 20 | cmd/core-keeper/core-keeper 21 | cmd/core-metadata/core-metadata 22 | cmd/security-proxy-auth/security-proxy-auth 23 | cmd/security-proxy-setup/security-proxy-setup 24 | cmd/security-file-token-provider/security-file-token-provider 25 | cmd/security-secretstore-setup/security-secretstore-setup 26 | cmd/support-logging/support-logging 27 | cmd/support-notifications/support-notifications 28 | cmd/support-scheduler/support-scheduler 29 | cmd/secrets-config/secrets-config 30 | cmd/security-bootstrapper/security-bootstrapper 31 | cmd/security-spiffe-token-provider/security-spiffe-token-provider 32 | 33 | docs/_build/ 34 | 35 | # log dirs 36 | **/logs 37 | 38 | # result files 39 | fuzz_test/fuzz_results/* 40 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 5m 3 | linters: 4 | disable: 5 | enable: 6 | - gosec 7 | linters-settings: 8 | gosec: 9 | excludes: 10 | # G115: integer overflow conversion 11 | # exclude the rule since it tends to be false positive 12 | - G115 13 | -------------------------------------------------------------------------------- /.hadolint.yml: -------------------------------------------------------------------------------- 1 | ignored: 2 | - DL3006 3 | - DL3008 4 | - DL3018 5 | - DL3059 6 | 7 | trustedRegistries: 8 | - docker.io 9 | - ghcr.io 10 | 11 | -------------------------------------------------------------------------------- /.sonarcloud.properties: -------------------------------------------------------------------------------- 1 | sonar.exclusions=.git/*,/bin/*,**/mocks/**/*,**/*_test.go,**/vendor/** 2 | sonar.tests=. 3 | sonar.test.inclusions=**/*_test.go 4 | sonar.test.exclusions=**/vendor/** 5 | -------------------------------------------------------------------------------- /ADOPTERS.md: -------------------------------------------------------------------------------- 1 | # EdgeX Foundry Adopters 2 | 3 | For a list of the latest EdgeX users, see our [EdgeX Users](https://www.edgexfoundry.org/ecosystem/users/) page on the EdgeX web site. 4 | 5 | | Organization and Web Site | EdgeX Ready Badged | 6 | | :--- | :--- | 7 | |[Accenture](https://www.accenture.com/)|| 8 | |[Beechwoods](https://www.beechwoods.com/)|| 9 | |[Canonical](https://ubuntu.com/)|| 10 | |[Dell Technologies](https://www.delltechnologies.com/)|| 11 | |[Equinix Metal](https://metal.equinix.com/)|| 12 | |[HP](www.hp.com)|| 13 | |[IBM](http://www.ibm.com)|| 14 | |[Intel](http://www.intel.com)|yes| 15 | |[IOTech](http://www.iotechsys.com)|yes| 16 | |[Jiangxing Intelligence](http://jiangxing.com)|| 17 | |[Net Foundry](http://netfoundry.io)|| 18 | |[Object Box](http://objectbox.io)|| 19 | |[ThunderSoft](http://www.thundersoft.com)|| 20 | |[Tibco](http://www.tibco.com)|| 21 | |[Wipro](http://www.wipro.com)|| 22 | |[Zededa](http://www.zededa.com)|| 23 | 24 | EdgeX adopters regularly present in our Adopter Series. Find EdgeX Adopter Series presentations on our [web site](https://www.edgexfoundry.org/ecosystem/adopter-series/) and [Wiki](https://wiki.edgexfoundry.org/display/FA/Vertical+Solutions+Working+Group#VerticalSolutionsWorkingGroup-UpcomingAdopterSeriesTalks). -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | # Governance 2 | 3 | Project governance as well as policies, procedures and instructions for contributing to EdgeX Foundry can be found on our Wiki site at the following locations: 4 | 5 | - [EdgeX Technical Steering Committee](https://wiki.edgexfoundry.org/pages/viewpage.action?pageId=329436) 6 | - [Contributor's Guide](https://wiki.edgexfoundry.org/display/FA/Contributor%27s+Guide) 7 | - [Contributor's Process](https://wiki.edgexfoundry.org/display/FA/Contributor%27s+Process) 8 | - [Technical Work](https://wiki.edgexfoundry.org/display/FA/Technical+Work+in+the+EdgeX+Foundry+Project) 9 | - [Contributors, Committers & Maintainers](https://wiki.edgexfoundry.org/pages/viewpage.action?pageId=21823860) -------------------------------------------------------------------------------- /Jenkinsfile: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // Copyright (c) 2023 IOTech Ltd 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | 18 | edgeXBuildGoParallel( 19 | project: 'edgex-go', 20 | dockerFileGlobPath: 'cmd/**/Dockerfile', 21 | testScript: 'make test', 22 | buildScript: 'make build', 23 | publishSwaggerDocs: true, 24 | swaggerApiFolders: ['openapi'] 25 | ) 26 | -------------------------------------------------------------------------------- /OWNERS.md: -------------------------------------------------------------------------------- 1 | # Repository Owners 2 | 3 | This repository is managed by the EdgeX Core Working Group. As such, the **Core Working Group** chairman is considered the "owner" of the repository and approves all committers of the repository. 4 | 5 | See the [project Wiki TSC page](https://wiki.edgexfoundry.org/pages/viewpage.action?pageId=329436#TechnicalSteeringCommittee(TSC)-WorkingGroups) for information on the current EdgeX TSC and who occupies the role of Core Working Group chair. 6 | 7 | For a complete list of current committers see: https://github.com/orgs/edgexfoundry/teams/edgex-go-committers/members. 8 | 9 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # EdgeX Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Not every EdgeX release receives security support. 6 | Please consult the EdgeX Foundry Wiki for information on the 7 | [EdgeX Long Term Support Policy](https://wiki.edgexfoundry.org/pages/viewpage.action?pageId=69173332). 8 | 9 | 10 | ## Reporting Vulnerabilities 11 | 12 | Instructions to report a vulnerability may be found on the 13 | [security page of the EdgeX Foundry Wiki](https://wiki.edgexfoundry.org/display/FA/Security). 14 | 15 | 16 | ## Questions 17 | 18 | Questions on the EdgeX security policy can be raised in the 19 | [EdgeX Security Working Group](https://wiki.edgexfoundry.org/display/FA/Security+Working+Group?src=contextnavpagetreemode) 20 | or to the 21 | [EdgeX Technical Steering Committee](https://wiki.edgexfoundry.org/pages/viewpage.action?pageId=329436&src=contextnavpagetreemode). -------------------------------------------------------------------------------- /bin/test-attribution-txt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | # get the directory of this script 4 | # snippet from https://stackoverflow.com/a/246128/10102404 5 | SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" 6 | GIT_ROOT=$(dirname "$SCRIPT_DIR") 7 | 8 | EXIT_CODE=0 9 | 10 | cd "$GIT_ROOT" 11 | 12 | if [ ! -f Attribution.txt ]; then 13 | echo "An Attribution.txt file is missing, please add" 14 | EXIT_CODE=1 15 | else 16 | # loop over every library in the go.mod file 17 | while IFS= read -r lib; do 18 | if ! grep -q "$lib" Attribution.txt; then 19 | echo "An attribution for $lib is missing from Attribution.txt, please add" 20 | # need to do this in a bash subshell, see SC2031 21 | (( EXIT_CODE=1 )) 22 | fi 23 | done < <(cat $GIT_ROOT/go.mod | grep -v 'module ' | grep -v TODO | grep '/' | sed 's/require //' | sed 's/replace //' | awk '{print $1}') 24 | fi 25 | -------------------------------------------------------------------------------- /cmd/core-command/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2018 Dell Inc. 3 | * Copyright (C) 2023 IOTech Ltd 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | *******************************************************************************/ 15 | 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | "github.com/edgexfoundry/edgex-go/internal/core/command" 23 | 24 | "github.com/labstack/echo/v4" 25 | ) 26 | 27 | func main() { 28 | ctx, cancel := context.WithCancel(context.Background()) 29 | command.Main(ctx, cancel, echo.New(), os.Args[1:]) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/core-command/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: INFO 3 | InsecureSecrets: 4 | mqtt: 5 | SecretName: mqtt 6 | SecretData: 7 | username: "" 8 | password: "" 9 | cacert: "" 10 | clientcert: "" 11 | clientkey: "" 12 | Service: 13 | Host: localhost 14 | Port: 59882 15 | StartupMsg: "This is the Core Command Microservice" 16 | Clients: 17 | core-metadata: 18 | Protocol: http 19 | Host: localhost 20 | Port: 59881 21 | SecurityOptions: 22 | Mode: "" 23 | OpenZitiController: "openziti:1280" 24 | ExternalMQTT: 25 | Enabled: false 26 | Url: "tcp://localhost:1883" 27 | ClientId: ex-core-command 28 | ConnectTimeout: 5s 29 | AutoReconnect: true 30 | KeepAlive: 10 31 | QoS: 0 32 | Retain: true 33 | SkipCertVerify: false 34 | SecretName: mqtt 35 | AuthMode: none 36 | Topics: 37 | CommandRequestTopic: edgex/command/request/# # for subscribing to 3rd party command requests 38 | CommandResponseTopicPrefix: edgex/command/response # for publishing responses back to 3rd party systems /// will be added to this publish topic prefix 39 | CommandQueryRequestTopic: edgex/commandquery/request/# # for subscribing to 3rd party command query request 40 | CommandQueryResponseTopic: edgex/commandquery/response # for publishing responses back to 3rd party systems 41 | 42 | MessageBus: 43 | Optional: 44 | ClientId: core-command 45 | 46 | 47 | -------------------------------------------------------------------------------- /cmd/core-common-config-bootstrapper/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dumb-init /bin/sh 2 | # ---------------------------------------------------------------------------------- 3 | # Copyright (c) 2023 Intel Corporation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # SPDX-License-Identifier: Apache-2.0 18 | # ---------------------------------------------------------------------------------- 19 | 20 | set -e 21 | 22 | "$@" && exec tail -f /dev/null 23 | -------------------------------------------------------------------------------- /cmd/core-common-config-bootstrapper/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2023 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | package main 15 | 16 | import ( 17 | "context" 18 | "os" 19 | 20 | "github.com/edgexfoundry/edgex-go/internal/core/common_config" 21 | ) 22 | 23 | func main() { 24 | ctx, cancel := context.WithCancel(context.Background()) 25 | common_config.Main(ctx, cancel, os.Args[1:]) 26 | } 27 | -------------------------------------------------------------------------------- /cmd/core-data/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright (c) 2019 Intel Corporation 4 | * Copyright (C) 2023 IOTech Ltd 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | "github.com/edgexfoundry/edgex-go/internal/core/data" 23 | 24 | "github.com/labstack/echo/v4" 25 | ) 26 | 27 | func main() { 28 | ctx, cancel := context.WithCancel(context.Background()) 29 | data.Main(ctx, cancel, echo.New(), os.Args[1:]) 30 | } 31 | -------------------------------------------------------------------------------- /cmd/core-data/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | MaxEventSize: 25000 # Defines the maximum event size in kilobytes 2 | Writable: 3 | LogLevel: "INFO" 4 | PersistData: true 5 | Telemetry: 6 | Metrics: # All service's metric names must be present in this list. 7 | EventsPersisted: false 8 | ReadingsPersisted: false 9 | # Tags: # Contains the service level tags to be attached to all the service's metrics 10 | ## Gateway="my-iot-gateway" # Tag must be added here or via Consul Env Override can only change existing value, not added new ones. 11 | EventPurge: false # Remove the related events and readings once received the device deletion system event 12 | 13 | Service: 14 | Port: 59880 15 | Host: "localhost" 16 | StartupMsg: "This is the Core Data Microservice" 17 | 18 | Clients: 19 | core-metadata: 20 | Protocol: http 21 | Host: localhost 22 | Port: 59881 23 | SecurityOptions: 24 | Mode: "" 25 | OpenZitiController: "openziti:1280" 26 | 27 | MessageBus: 28 | Optional: 29 | ClientId: "core-data" 30 | 31 | Retention: 32 | Interval: 10m # Purging interval defines when the database should be rid of readings above the high watermark. 33 | DefaultMaxCap: -1 # The maximum capacity defines where the high watermark of readings should be detected for purging the amount of the reading to the minimum capacity. 34 | DefaultMinCap: 1 # The minimum capacity defines where the total count of readings should be returned to during purging. 35 | DefaultDuration: "168h" # The duration to keep the event, the expired events should be detected for purging, but the service will still keep the number of MinCap. 36 | 37 | -------------------------------------------------------------------------------- /cmd/core-keeper/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "os" 11 | 12 | "github.com/edgexfoundry/edgex-go/internal/core/keeper" 13 | 14 | "github.com/labstack/echo/v4" 15 | ) 16 | 17 | func main() { 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | keeper.Main(ctx, cancel, echo.New(), os.Args[1:]) 20 | } 21 | -------------------------------------------------------------------------------- /cmd/core-metadata/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2018 Dell Inc. 3 | * Copyright (C) 2023 IOTech Ltd 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | *******************************************************************************/ 15 | package main 16 | 17 | import ( 18 | "context" 19 | "os" 20 | 21 | "github.com/edgexfoundry/edgex-go/internal/core/metadata" 22 | 23 | "github.com/labstack/echo/v4" 24 | ) 25 | 26 | func main() { 27 | ctx, cancel := context.WithCancel(context.Background()) 28 | metadata.Main(ctx, cancel, echo.New(), os.Args[1:]) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/core-metadata/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: INFO 3 | ProfileChange: 4 | StrictDeviceProfileChanges: false 5 | StrictDeviceProfileDeletes: false 6 | UoM: 7 | Validation: false 8 | MaxDevices: 0 9 | MaxResources: 0 10 | 11 | Service: 12 | Host: localhost 13 | Port: 59881 14 | StartupMsg: "This is the EdgeX Core Metadata Microservice" 15 | UoM: 16 | UoMFile: ./res/uom.yaml 17 | 18 | MessageBus: 19 | Optional: 20 | ClientId: core-metadata 21 | -------------------------------------------------------------------------------- /cmd/core-metadata/res/uom.yaml: -------------------------------------------------------------------------------- 1 | Source: reference to source for all UoM if not specified below 2 | Units: 3 | temperature: 4 | Source: www.weather.com 5 | Values: 6 | - C 7 | - F 8 | - K 9 | weights: 10 | Source: www.usa.gov/federal-agencies/weights-and-measures-division 11 | Values: 12 | - lbs 13 | - ounces 14 | - kilos 15 | - grams 16 | -------------------------------------------------------------------------------- /cmd/secrets-config/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package main 8 | 9 | import ( 10 | "context" 11 | "os" 12 | 13 | "github.com/edgexfoundry/edgex-go/internal/security/config" 14 | ) 15 | 16 | func main() { 17 | os.Setenv("LOGLEVEL", "ERROR") // Workaround for https://github.com/edgexfoundry/edgex-go/issues/2922 18 | ctx, cancel := context.WithCancel(context.Background()) 19 | exitStatusCode := config.Main(ctx, cancel, os.Args[1:]) 20 | os.Exit(exitStatusCode) 21 | } 22 | -------------------------------------------------------------------------------- /cmd/secrets-config/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2023 Intel Corporation 3 | # Copyright (c) 2024 IOTech Ltd 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | # 7 | 8 | LogLevel: DEBUG 9 | SecretStore: 10 | Type: openbao 11 | Protocol: http 12 | Host: localhost 13 | Port: 8200 14 | CertPath: "" 15 | CaFilePath: "" 16 | CertFilePath: "" 17 | KeyFilePath: "" 18 | # for root token use: /openbao/config/assets 19 | # for service token use: /tmp/edgex/secrets/security-proxy-setup 20 | TokenFolderPath: /openbao/config/assets 21 | # for root token use: resp-init.json 22 | # for service token use: secrets-token.json 23 | TokenFile: resp-init.json 24 | 25 | # FIXME whittle this down more 26 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/entrypoint-scripts/kuiper_wait_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------------- 3 | # Copyright (c) 2021 Intel Corporation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # SPDX-License-Identifier: Apache-2.0 18 | # ---------------------------------------------------------------------------------- 19 | 20 | # This is customized entrypoint script for other Edgex services. 21 | # In particular, it waits for the ReadyToRunPort raised to be ready to roll 22 | # 23 | # Note: 24 | # Since the entrypoint script is overridden, user should also override the command 25 | # so that the $@ is set appropriately on the run-time. 26 | # 27 | 28 | set -e 29 | 30 | # env settings are populated from env files of docker-compose 31 | 32 | echo "Script for waiting on security bootstrapping ready-to-run" 33 | 34 | # gating on the ready-to-run port 35 | echo "$(date) Executing waitFor with $@ waiting on tcp://${STAGEGATE_BOOTSTRAPPER_HOST}:${STAGEGATE_READY_TORUNPORT}" 36 | /edgex-init/security-bootstrapper --configDir=/edgex-init/res waitFor \ 37 | -uri tcp://"${STAGEGATE_BOOTSTRAPPER_HOST}":"${STAGEGATE_READY_TORUNPORT}" \ 38 | -timeout "${STAGEGATE_WAITFOR_TIMEOUT}" 39 | 40 | echo "$(date) Starting Kuiper ..." 41 | exec /usr/bin/docker-entrypoint.sh ./bin/kuiperd 42 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/entrypoint-scripts/nginx_wait_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------------- 3 | # Copyright (c) 2023 Intel Corporation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # SPDX-License-Identifier: Apache-2.0 18 | # ---------------------------------------------------------------------------------- 19 | 20 | set -e 21 | 22 | # env settings are populated from env files of docker-compose 23 | 24 | echo "Awaiting proxy-setup to finish before starting NGINX" 25 | 26 | # gating on the proxy-setup ready port 27 | echo "$(date) Executing waitFor with waiting on tcp://${PROXY_SETUP_HOST}:${STAGEGATE_PROXYSETUP_READYPORT}" 28 | /edgex-init/security-bootstrapper --configDir=/edgex-init/res waitFor \ 29 | -uri tcp://"${PROXY_SETUP_HOST}":"${STAGEGATE_PROXYSETUP_READYPORT}" \ 30 | -timeout "${STAGEGATE_WAITFOR_TIMEOUT}" 31 | 32 | echo "$(date) Starting NGINX ..." 33 | exec /docker-entrypoint.sh "$@" 34 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/entrypoint-scripts/ready_to_run_wait_install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dumb-init /bin/sh 2 | # ---------------------------------------------------------------------------------- 3 | # Copyright (c) 2021 Intel Corporation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # SPDX-License-Identifier: Apache-2.0 18 | # ---------------------------------------------------------------------------------- 19 | 20 | # This is customized entrypoint script for other Edgex services. 21 | # In particular, it waits for the ReadyToRunPort raised to be ready to roll 22 | # 23 | # Note: 24 | # Since the entrypoint script is overridden, user should also override the command 25 | # so that the $@ is set appropriately on the run-time. 26 | # 27 | 28 | set -e 29 | 30 | # env settings are populated from env files of docker-compose 31 | 32 | echo "Script for waiting on security bootstrapping ready-to-run" 33 | 34 | # gating on the ready-to-run port 35 | echo "$(date) Executing waitFor with $@ waiting on tcp://${STAGEGATE_BOOTSTRAPPER_HOST}:${STAGEGATE_READY_TORUNPORT}" 36 | /edgex-init/security-bootstrapper --configDir=/edgex-init/res waitFor \ 37 | -uri tcp://"${STAGEGATE_BOOTSTRAPPER_HOST}":"${STAGEGATE_READY_TORUNPORT}" \ 38 | -timeout "${STAGEGATE_WAITFOR_TIMEOUT}" 39 | 40 | echo "$(date) Starting $@ ..." 41 | exec "$@" 42 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "os" 20 | 21 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper" 22 | ) 23 | 24 | func main() { 25 | // When running security-bootstrapper's subcommands, we don't want the side effect of 26 | // the env var. EDGEX_PROFILE overriding so that unset the env can avoid the config's resource 27 | // directory path of the security-bootstrapper itself being modified. 28 | _ = os.Unsetenv("EDGEX_PROFILE") 29 | 30 | ctx, cancel := context.WithCancel(context.Background()) 31 | bootstrapper.Main(ctx, cancel, os.Args[1:]) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/res-bootstrap-mosquitto/configuration.yaml: -------------------------------------------------------------------------------- 1 | ################################################################################# 2 | # Copyright 2023 Intel Corp. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | # in compliance with the License. You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software distributed under the License 10 | # is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | # or implied. See the License for the specific language governing permissions and limitations under 12 | # the License. 13 | # 14 | ################################################################################# 15 | 16 | LogLevel: INFO 17 | SecureMosquitto: 18 | Port: 1883 19 | PasswordFile: /mosquitto/config/pwdfile 20 | BrokerConfigFile: /mosquitto/config/mosquitto.conf 21 | SecretName: message-bus 22 | 23 | 24 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/res-bootstrap-postgres/configuration.yaml: -------------------------------------------------------------------------------- 1 | LogLevel: INFO 2 | Database: 3 | Host: localhost 4 | Name: "edgex_db" 5 | Password: "" # Will be set from value in Vault 6 | Username: "" # Will be set from value in Vault 7 | Port: 5432 8 | Timeout: "5s" 9 | Type: postgres 10 | DatabaseConfig: 11 | Path: /postgres-init-scripts 12 | Name: create-users.sh 13 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/res-bootstrap-redis/configuration.yaml: -------------------------------------------------------------------------------- 1 | LogLevel: INFO 2 | Database: 3 | Host: localhost 4 | Name: "" # Unused 5 | Password: "" # Will be set from value in Vault 6 | Username: "" # Will be set from value in Vault 7 | Port: 6379 8 | Timeout: "5s" 9 | Type: redisdb 10 | DatabaseConfig: 11 | Path: /path/to/redis/conf/dir 12 | Name: redis.conf 13 | MaxClients: 1000 14 | -------------------------------------------------------------------------------- /cmd/security-bootstrapper/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | LogLevel: INFO 2 | StageGate: 3 | BootStrapper: 4 | Host: edgex-security-bootstrapper 5 | StartPort: 54321 6 | Ready: 7 | ToRunPort: 54329 8 | SecretStoreSetup: 9 | Host: edgex-secretstore-setup 10 | Tokens: 11 | ReadyPort: 54322 12 | Database: # this is intended to be the same as Database.Host/.Port for other services 13 | Host: edgex-postgres 14 | Port: 5432 15 | ReadyPort: 54323 16 | WaitFor: 17 | Timeout: 10s 18 | RetryInterval: 1s 19 | 20 | # this configuration is just part of the whole go-mod-bootstrap's secret store to have 21 | # protocol, host, and port of secretstore using in the security-bootstrapper 22 | # we are not really using the secret store provider from go-mod-bootstrap in the code 23 | SecretStore: 24 | Type: openbao 25 | Protocol: http 26 | Host: localhost 27 | Port: 8200 28 | -------------------------------------------------------------------------------- /cmd/security-file-token-provider/README.md: -------------------------------------------------------------------------------- 1 | # Integration testing instructions for security-file-token-provider 2 | 3 | ## Build 4 | 5 | Use Makefile in the base directory of this repository to build the docker image of security-secretes-setup: 6 | 7 | ```sh 8 | make -C ../.. cmd/security-file-token-provider/security-file-token-provider 9 | ``` 10 | 11 | It should create an executable named `security-file-token-provider` in the current directory. 12 | 13 | 14 | ## Installation 15 | 16 | It is intended that this utility be invoked as the `tokenprovider` of `security-secretstore-setup` 17 | after unsealing of the secret store has been completed. 18 | -------------------------------------------------------------------------------- /cmd/security-file-token-provider/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | "github.com/edgexfoundry/edgex-go/internal/security/fileprovider" 23 | ) 24 | 25 | func main() { 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | fileprovider.Main(ctx, cancel, os.Args[1:]) 28 | } 29 | -------------------------------------------------------------------------------- /cmd/security-proxy-auth/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/dumb-init /bin/sh 2 | # ---------------------------------------------------------------------------------- 3 | # Copyright (c) 2023 Intel Corporation 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # SPDX-License-Identifier: Apache-2.0 18 | # ---------------------------------------------------------------------------------- 19 | 20 | set -e 21 | 22 | "$@" && exec tail -f /dev/null 23 | -------------------------------------------------------------------------------- /cmd/security-proxy-auth/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2023 Intel Corporation 3 | * Copyright (C) 2023 IOTech Ltd 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | *******************************************************************************/ 15 | package main 16 | 17 | import ( 18 | "context" 19 | "os" 20 | 21 | "github.com/edgexfoundry/edgex-go/internal/security/proxyauth" 22 | 23 | "github.com/labstack/echo/v4" 24 | ) 25 | 26 | func main() { 27 | ctx, cancel := context.WithCancel(context.Background()) 28 | proxyauth.Main(ctx, cancel, echo.New(), os.Args[1:]) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/security-proxy-auth/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: INFO 3 | Service: 4 | Host: localhost 5 | Port: 59842 6 | StartupMsg: "This is the proxy authentication microservice" 7 | -------------------------------------------------------------------------------- /cmd/security-proxy-setup/README.md: -------------------------------------------------------------------------------- 1 | # EdgeX Foundry Security Service - Security Proxy Setup 2 | [![license](https://img.shields.io/badge/license-Apache%20v2.0-blue.svg)](LICENSE) 3 | 4 | This folder builds a container that configures the NGINX reverse proxy and contains a copy of the `secrets-config` utility. 5 | 6 | The `security-proxy-setup` binary that configured the Kong reverse proxy is removed with EdgeX 3.0 7 | 8 | ## Docker Build 9 | 10 | Go to the root directory of the repository and use the Makefile to build the docker container image for `security-proxy-setup`: 11 | 12 | ```sh 13 | $ make docker_security_proxy_setup 14 | ``` 15 | 16 | It should create a docker image with the name `edgexfoundry/docker_security_proxy_setup:-dev` if successfully built. 17 | -------------------------------------------------------------------------------- /cmd/security-secretstore-setup/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Dell Inc. 3 | * Copyright (c) 2025 IOTech Ltd 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | * 15 | * @author: Alain Pulluelo, ForgeRock AS 16 | * @author: Tingyu Zeng, Dell 17 | * @author: Daniel Harms, Dell 18 | * 19 | *******************************************************************************/ 20 | 21 | package main 22 | 23 | import ( 24 | "context" 25 | "os" 26 | 27 | "github.com/edgexfoundry/edgex-go/internal/security/secretstore" 28 | ) 29 | 30 | func main() { 31 | ctx, cancel := context.WithCancel(context.Background()) 32 | secretstore.Main(ctx, cancel, os.Args[1:]) 33 | } 34 | -------------------------------------------------------------------------------- /cmd/security-secretstore-setup/res-file-token-provider/configuration.yaml: -------------------------------------------------------------------------------- 1 | LogLevel: DEBUG 2 | SecretStore: 3 | Type: openbao 4 | Protocol: http 5 | Host: localhost 6 | Port: 8200 7 | ServerName: "" 8 | CaFilePath: "" 9 | TokenFileProvider: 10 | PrivilegedTokenPath: /run/edgex/secrets/tokenprovider/secrets-token.json 11 | ConfigFile: res-file-token-provider/token-config.json 12 | OutputDir: /tmp/edgex/secrets 13 | OutputFilename: secrets-token.json 14 | DefaultTokenTTL: 1h 15 | DefaultJWTTTL: 15m 16 | DefaultJWTAudience: edgex 17 | UserPassMountPoint: userpass 18 | -------------------------------------------------------------------------------- /cmd/security-spiffe-token-provider/README.md: -------------------------------------------------------------------------------- 1 | # Notes for testing out the spiffe-token-provider 2 | 3 | Developers can utilize the docker-compose with edgex-security-test-client running together with spiffe related services. 4 | To start the spiffe related services, please go to `edgex-compose` repository and use the `make run` utility under 5 | the `compose-builder` directory. The spiffe related services can be started with adding an option `delayed-start` after 6 | `make run`; e.g. `make run dev delayed-start`. 7 | Once spiffe related services are started, then use a command line console to do the following steps: 8 | 9 | 1. docker exec -ti edgex-security-test-client sh -l 10 | 1. apk update && apk --no-cache --update add curl 11 | 1. cd /tmp 12 | 1. spire-agent api fetch x509 -socketPath /tmp/edgex/secrets/spiffe/public/api.sock -write /tmp 13 | 1. curl -kiv -d service_key=security-test-client -d known_secret_names=redisdb --cert svid.0.pem --key svid.0.key -X POST "https://edgex-security-spiffe-token-provider:59841/api/v3/gettoken?raw_token=true" 14 | 15 | If https request with curl command above runs successfully, we should see the secret store raw token returned. 16 | -------------------------------------------------------------------------------- /cmd/security-spiffe-token-provider/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2022 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package main 17 | 18 | import ( 19 | "context" 20 | "os" 21 | 22 | "github.com/edgexfoundry/edgex-go/internal/security/spiffetokenprovider" 23 | ) 24 | 25 | func main() { 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | spiffetokenprovider.Main(ctx, cancel, os.Args[1:]) 28 | } 29 | -------------------------------------------------------------------------------- /cmd/security-spiffe-token-provider/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: DEBUG 3 | Service: 4 | Host: localhost 5 | Port: 59841 6 | StartupMsg: "This is the SPIFFE token provider microservice" 7 | TokenConfig: 8 | DefaultTokenTTL: 1h 9 | DefaultJWTTTL: 15m 10 | DefaultJWTAudience: edgex 11 | UserPassMountPoint: userpass 12 | Spiffe: 13 | EndpointSocket: /tmp/edgex/secrets/spiffe/public/api.sock 14 | TrustDomain: edgexfoundry.org 15 | -------------------------------------------------------------------------------- /cmd/security-spire-agent/Dockerfile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # Copyright 2023 Intel Corporation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # ---------------------------------------------------------------------------------- 17 | 18 | # Build utility container 19 | ARG BUILDER_BASE=golang:1.23-alpine3.20 20 | FROM ${BUILDER_BASE} AS builder 21 | 22 | FROM ghcr.io/spiffe/spire-server:1.11.1 as spire_server 23 | FROM ghcr.io/spiffe/spire-agent:1.11.1 as spire_agent 24 | 25 | # Deployment image 26 | FROM alpine:3.20 27 | 28 | LABEL license='SPDX-License-Identifier: Apache-2.0' \ 29 | copyright='Copyright (c) 2022 Intel Corporation' 30 | 31 | RUN apk update && apk --no-cache --update add dumb-init openssl gcompat 32 | # Ensure using latest versions of all installed packages to avoid any recent CVEs 33 | RUN apk --no-cache upgrade 34 | 35 | COPY --from=spire_agent /opt/spire/bin/spire-agent /usr/local/bin 36 | COPY --from=spire_server /opt/spire/bin/spire-server /usr/local/bin 37 | 38 | COPY Attribution.txt / 39 | COPY security.txt / 40 | 41 | COPY cmd/security-spire-agent/docker-entrypoint.sh /usr/local/bin/ 42 | COPY cmd/security-spire-agent/agent.conf /usr/local/etc/spire/agent.conf.tpl 43 | COPY cmd/security-spire-agent/openssl.conf /usr/local/etc/ 44 | 45 | ENTRYPOINT [ "/usr/bin/dumb-init" ] 46 | CMD [ "--verbose", "docker-entrypoint.sh" ] 47 | -------------------------------------------------------------------------------- /cmd/security-spire-agent/agent.conf: -------------------------------------------------------------------------------- 1 | agent { 2 | data_dir = "/srv/spiffe/agent/data" 3 | log_file = "/dev/stdout" 4 | log_level = "DEBUG" 5 | server_address = "SPIFFE_SERVER_HOST" 6 | server_port = SPIFFE_SERVER_PORT 7 | socket_path = "SPIFFE_ENDPOINTSOCKET" 8 | trust_bundle_path = "SPIFFE_TRUSTBUNDLE_PATH" 9 | trust_domain = "SPIFFE_TRUSTDOMAIN" 10 | } 11 | 12 | plugins { 13 | KeyManager "disk" { 14 | plugin_data { 15 | directory = "/srv/spiffe/agent" 16 | } 17 | } 18 | 19 | NodeAttestor "x509pop" { 20 | plugin_data { 21 | private_key_path = "/srv/spiffe/agent/agent.key" 22 | certificate_path = "/srv/spiffe/agent/agent.crt" 23 | } 24 | } 25 | 26 | WorkloadAttestor "unix" { 27 | plugin_data { 28 | discover_workload_path = true 29 | } 30 | } 31 | 32 | WorkloadAttestor "docker" { 33 | plugin_data {} 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cmd/security-spire-agent/openssl.conf: -------------------------------------------------------------------------------- 1 | [req] 2 | prompt = no 3 | distinguished_name = req_distingiushed_name 4 | 5 | [ req_distingiushed_name ] 6 | commonName = 7 | 8 | [ ca_ext ] 9 | basicConstraints = critical,CA:TRUE,pathlen:1 10 | keyUsage = critical, digitalSignature, keyCertSign, cRLSign 11 | subjectKeyIdentifier = hash 12 | authorityKeyIdentifier = keyid:always 13 | 14 | # unused 15 | [ server_ext ] 16 | basicConstraints = critical,CA:FALSE 17 | keyUsage = digitalSignature, keyAgreement, keyEncipherment 18 | extendedKeyUsage = serverAuth 19 | subjectKeyIdentifier = hash 20 | authorityKeyIdentifier = keyid:always 21 | subjectAltName = ${ENV::SAN} 22 | 23 | # unused 24 | [ client_ext ] 25 | basicConstraints = critical,CA:FALSE 26 | keyUsage = digitalSignature, keyAgreement 27 | extendedKeyUsage = clientAuth 28 | subjectKeyIdentifier = hash 29 | authorityKeyIdentifier = keyid:always 30 | 31 | # Agent cert requirements 32 | # https://github.com/spiffe/spire/blob/v1.2.0/doc/plugin_agent_nodeattestor_x509pop.md 33 | 34 | [ agent_ext ] 35 | basicConstraints = critical,CA:FALSE 36 | keyUsage = digitalSignature 37 | subjectKeyIdentifier = hash 38 | authorityKeyIdentifier = keyid:always 39 | -------------------------------------------------------------------------------- /cmd/security-spire-config/Dockerfile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # Copyright 2023 Intel Corporation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # ---------------------------------------------------------------------------------- 17 | 18 | # Build utility container 19 | ARG BUILDER_BASE=golang:1.23-alpine3.20 20 | FROM ${BUILDER_BASE} AS builder 21 | 22 | FROM ghcr.io/spiffe/spire-server:1.11.1 as spire_server 23 | 24 | # Deployment image 25 | FROM alpine:3.20 26 | 27 | LABEL license='SPDX-License-Identifier: Apache-2.0' \ 28 | copyright='Copyright (c) 2022 Intel Corporation' 29 | 30 | RUN apk update && apk --no-cache --update add dumb-init gcompat 31 | # Ensure using latest versions of all installed packages to avoid any recent CVEs 32 | RUN apk --no-cache upgrade 33 | 34 | COPY --from=spire_server /opt/spire/bin/spire-server /usr/local/bin 35 | 36 | COPY Attribution.txt / 37 | COPY security.txt / 38 | COPY cmd/security-spire-config/docker-entrypoint.sh /usr/local/bin/ 39 | 40 | WORKDIR /usr/local/etc/spiffe-scripts.d 41 | COPY cmd/security-spire-config/seed_builtin_entries.sh /usr/local/etc/spiffe-scripts.d 42 | 43 | WORKDIR / 44 | 45 | ENTRYPOINT [ "/usr/bin/dumb-init" ] 46 | CMD [ "--verbose", "docker-entrypoint.sh" ] 47 | -------------------------------------------------------------------------------- /cmd/security-spire-config/docker-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -xe 2 | # 3 | # ---------------------------------------------------------------------------------- 4 | # Copyright (c) 2022 Intel Corporation 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # SPDX-License-Identifier: Apache-2.0 19 | # ---------------------------------------------------------------------------------- 20 | # 21 | 22 | umask 027 23 | 24 | export SPIFFE_SERVER_SOCKET 25 | export SPIFFE_EDGEX_SVID_BASE 26 | export SPIFFE_TRUSTDOMAIN 27 | 28 | : ${SPIFFE_SERVER_SOCKET:=/tmp/edgex/secrets/spiffe/private/api.sock} 29 | : ${SPIFFE_EDGEX_SVID_BASE:=spiffe://edgexfoundry.org/service} 30 | : ${SPIFFE_TRUSTDOMAIN:=edgexfoundry.org} 31 | 32 | : ${SPIFFE_AGENT0_CN:=agent0} 33 | : ${SPIFFE_PARENTID:=spiffe://${SPIFFE_TRUSTDOMAIN}/spire/agent/x509pop/cn/${SPIFFE_AGENT0_CN}} 34 | 35 | # Wait for agent CA creation 36 | 37 | while test ! -S "${SPIFFE_SERVER_SOCKET}"; do 38 | echo "Waiting for ${SPIFFE_SERVER_SOCKET}" 39 | sleep 1 40 | done 41 | 42 | # Run config scripts 43 | 44 | for script in /usr/local/etc/spiffe-scripts.d/* ; do 45 | test -x "${script}" && ${script} "${SPIFFE_PARENTID}" 46 | done 47 | 48 | exec tail -f /dev/null 49 | -------------------------------------------------------------------------------- /cmd/security-spire-server/Dockerfile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # Copyright 2022 Intel Corporation 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | # 16 | # ---------------------------------------------------------------------------------- 17 | 18 | # Build utility container 19 | ARG BUILDER_BASE=golang:1.23-alpine3.20 20 | FROM ${BUILDER_BASE} AS builder 21 | 22 | FROM ghcr.io/spiffe/spire-server:1.11.1 as spire_server 23 | 24 | # Deployment image 25 | FROM alpine:3.20 26 | 27 | LABEL license='SPDX-License-Identifier: Apache-2.0' \ 28 | copyright='Copyright (c) 2022 Intel Corporation' 29 | 30 | RUN apk update && apk --no-cache --update add dumb-init openssl gcompat 31 | # Ensure using latest versions of all installed packages to avoid any recent CVEs 32 | RUN apk --no-cache upgrade 33 | 34 | COPY --from=spire_server /opt/spire/bin/spire-server /usr/local/bin 35 | 36 | COPY Attribution.txt / 37 | COPY security.txt / 38 | 39 | COPY cmd/security-spire-server/docker-entrypoint.sh /usr/local/bin/ 40 | COPY cmd/security-spire-server/server.conf /usr/local/etc/spire/server.conf.tpl 41 | COPY cmd/security-spire-server/openssl.conf /usr/local/etc/ 42 | 43 | ENTRYPOINT [ "/usr/bin/dumb-init" ] 44 | CMD [ "--verbose", "docker-entrypoint.sh" ] 45 | -------------------------------------------------------------------------------- /cmd/security-spire-server/openssl.conf: -------------------------------------------------------------------------------- 1 | [req] 2 | prompt = no 3 | distinguished_name = req_distingiushed_name 4 | 5 | [ req_distingiushed_name ] 6 | commonName = 7 | 8 | [ ca_ext ] 9 | basicConstraints = critical,CA:TRUE,pathlen:1 10 | keyUsage = critical, digitalSignature, keyCertSign, cRLSign 11 | subjectKeyIdentifier = hash 12 | authorityKeyIdentifier = keyid:always 13 | 14 | # unused 15 | [ server_ext ] 16 | basicConstraints = critical,CA:FALSE 17 | keyUsage = digitalSignature, keyAgreement, keyEncipherment 18 | extendedKeyUsage = serverAuth 19 | subjectKeyIdentifier = hash 20 | authorityKeyIdentifier = keyid:always 21 | subjectAltName = ${ENV::SAN} 22 | 23 | # unused 24 | [ client_ext ] 25 | basicConstraints = critical,CA:FALSE 26 | keyUsage = digitalSignature, keyAgreement 27 | extendedKeyUsage = clientAuth 28 | subjectKeyIdentifier = hash 29 | authorityKeyIdentifier = keyid:always 30 | 31 | # Agent cert requirements 32 | # https://github.com/spiffe/spire/blob/v1.2.0/doc/plugin_agent_nodeattestor_x509pop.md 33 | 34 | [ agent_ext ] 35 | basicConstraints = critical,CA:FALSE 36 | keyUsage = digitalSignature 37 | subjectKeyIdentifier = hash 38 | authorityKeyIdentifier = keyid:always 39 | -------------------------------------------------------------------------------- /cmd/security-spire-server/server.conf: -------------------------------------------------------------------------------- 1 | server { 2 | audit_log_enabled = true 3 | bind_port = SPIFFE_SERVER_PORT 4 | log_file = "/dev/stdout" 5 | log_level = "DEBUG" 6 | data_dir = "/srv/spiffe/server/data" 7 | default_x509_svid_ttl = "1h" 8 | default_jwt_svid_ttl = "5m" 9 | ca_key_type = "ec-p384" 10 | ca_subject { 11 | country = ["US"] 12 | organization = ["SPIFFE"] 13 | common_name = "" 14 | } 15 | socket_path = "SPIFFE_SERVER_SOCKET" 16 | trust_domain = "SPIFFE_TRUSTDOMAIN" 17 | } 18 | 19 | plugins { 20 | DataStore "sql" { 21 | plugin_data { 22 | database_type = "sqlite3" 23 | connection_string = "/srv/spiffe/server/datastore.sqlite3" 24 | } 25 | } 26 | 27 | NodeAttestor "x509pop" { 28 | plugin_data { 29 | ca_bundle_path = "/srv/spiffe/ca/public/agent-ca.crt" 30 | agent_path_template = "/{{ .PluginName}}/cn/{{ .Subject.CommonName }}" 31 | } 32 | } 33 | 34 | KeyManager "disk" { 35 | plugin_data = { 36 | keys_path = "/srv/spiffe/server/keys.json" 37 | } 38 | } 39 | 40 | UpstreamAuthority "disk" { 41 | plugin_data = { 42 | cert_file_path = "/srv/spiffe/ca/public/ca.crt" 43 | key_file_path = "/srv/spiffe/ca/private/ca.key" 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /cmd/support-notifications/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (C) 2023 IOTech Ltd 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | * 16 | * @microservice: support-notifications 17 | * @author: Jim White, Dell Technologies 18 | * @version: 0.5.0 19 | *******************************************************************************/ 20 | 21 | // main is the central entry point for the application and calls all the startup logic. 22 | package main 23 | 24 | import ( 25 | "context" 26 | "os" 27 | 28 | "github.com/edgexfoundry/edgex-go/internal/support/notifications" 29 | 30 | "github.com/labstack/echo/v4" 31 | ) 32 | 33 | func main() { 34 | ctx, cancel := context.WithCancel(context.Background()) 35 | notifications.Main(ctx, cancel, echo.New(), os.Args[1:]) 36 | } 37 | -------------------------------------------------------------------------------- /cmd/support-notifications/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: INFO 3 | ResendLimit: 2 4 | ResendInterval: 5s 5 | InsecureSecrets: 6 | SMTP: 7 | SecretName: smtp 8 | SecretData: 9 | username: username@mail.example.com 10 | password: '' 11 | 12 | Service: 13 | Host: localhost 14 | Port: 59860 15 | StartupMsg: "This is the Support Notifications Microservice" 16 | Smtp: 17 | Host: my.email.server 18 | Port: 587 19 | Sender: some.email.address 20 | EnableSelfSignedCert: false 21 | Subject: EdgeX Notification 22 | # SecretName is used to specify the secret name to store the credential(username and password) for connecting the SMTP server 23 | # User need to store the credential via the /secret API before sending the email notification 24 | SecretName: smtp 25 | # AuthMode is the SMTP authentication mechanism. Currently, "usernamepassword" is the only AuthMode supported by this service, and the secret keys are "username" and "password". 26 | AuthMode: usernamepassword 27 | 28 | MessageBus: 29 | Optional: 30 | ClientId: support-notifications 31 | 32 | Retention: 33 | Enabled: false 34 | Interval: 30m # Purging interval defines when the database should be rid of notifications above the high watermark. 35 | MaxCap: 5000 # The maximum capacity defines where the high watermark of notifications should be detected for purging the amount of the notifications to the minimum capacity. 36 | MinCap: 4000 # The minimum capacity defines where the total count of notifications should be returned to during purging. 37 | -------------------------------------------------------------------------------- /cmd/support-scheduler/main.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2024 IOTech Ltd 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "os" 20 | 21 | "github.com/edgexfoundry/edgex-go/internal/support/scheduler" 22 | 23 | "github.com/labstack/echo/v4" 24 | ) 25 | 26 | func main() { 27 | ctx, cancel := context.WithCancel(context.Background()) 28 | scheduler.Main(ctx, cancel, echo.New(), os.Args[1:]) 29 | } 30 | -------------------------------------------------------------------------------- /cmd/support-scheduler/res/configuration.yaml: -------------------------------------------------------------------------------- 1 | Writable: 2 | LogLevel: INFO 3 | InsecureSecrets: 4 | DB: 5 | SecretName: postgres 6 | SecretData: 7 | username: postgres 8 | password: postgres 9 | 10 | Service: 11 | Host: localhost 12 | Port: 59863 13 | StartupMsg: This is the Support Scheduler Microservice 14 | 15 | Clients: 16 | core-command: 17 | Protocol: http 18 | Host: localhost 19 | Port: 59882 20 | SecurityOptions: 21 | Mode: "" 22 | OpenZitiController: "openziti:1280" 23 | 24 | MessageBus: 25 | Optional: 26 | ClientId: support-scheduler 27 | 28 | Retention: 29 | Enabled: true 30 | Interval: 24h # Purging interval defines when the database should be rid of records above the high watermark. 31 | MaxCap: 10000 # The maximum capacity defines where the high watermark of records should be detected for purging the amount of the records to the minimum capacity. 32 | MinCap: 8000 # The minimum capacity defines where the total count of records should be returned to during purging. 33 | -------------------------------------------------------------------------------- /internal/constants.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2018 Dell Inc. 3 | *Copyright (c) 2023 Intel Corporation 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package internal 16 | 17 | const ( 18 | BootTimeoutDefault = BootTimeoutSecondsDefault * 1000 19 | BootTimeoutSecondsDefault = 30 20 | BootRetrySecondsDefault = 1 21 | LogDurationKey = "duration" 22 | ) 23 | 24 | const ( 25 | ConfigProviderEnvVar = "edgex_configuration_provider" 26 | WritableKey = "/Writable" 27 | ) 28 | 29 | const ( 30 | AuthHeaderTitle = "Authorization" 31 | BearerLabel = "Bearer " 32 | ) 33 | const ( 34 | BootstrapMessageBusServiceKey = "security-bootstrapper-messagebus" 35 | ) 36 | -------------------------------------------------------------------------------- /internal/core/command/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package container 16 | 17 | import ( 18 | "github.com/edgexfoundry/edgex-go/internal/core/command/config" 19 | 20 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 21 | ) 22 | 23 | // ConfigurationName contains the name of command's config.ConfigurationStruct implementation in the DIC. 24 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 25 | 26 | // ConfigurationFrom helper function queries the DIC and returns command's config.ConfigurationStruct implementation. 27 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 28 | return get(ConfigurationName).(*config.ConfigurationStruct) 29 | } 30 | -------------------------------------------------------------------------------- /internal/core/command/router.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2021-2025 IOTech Ltd 3 | // Copyright (C) 2023 Intel Corporation 4 | // 5 | // SPDX-License-Identifier: Apache-2.0 6 | 7 | package command 8 | 9 | import ( 10 | "github.com/edgexfoundry/edgex-go" 11 | commandController "github.com/edgexfoundry/edgex-go/internal/core/command/controller/http" 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/controller" 13 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/handlers" 14 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 15 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 16 | 17 | "github.com/labstack/echo/v4" 18 | ) 19 | 20 | func LoadRestRoutes(r *echo.Echo, dic *di.Container, serviceName string) { 21 | authenticationHook := handlers.AutoConfigAuthenticationFunc(dic) 22 | 23 | // Common 24 | _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) 25 | 26 | // Command 27 | cmd := commandController.NewCommandController(dic) 28 | r.GET(common.ApiAllDeviceRoute, cmd.AllCommands, authenticationHook) 29 | r.GET(common.ApiDeviceByNameRoute, cmd.CommandsByDeviceName, authenticationHook) 30 | r.GET(common.ApiDeviceNameCommandNameRoute, cmd.IssueGetCommandByName, authenticationHook) 31 | r.PUT(common.ApiDeviceNameCommandNameRoute, cmd.IssueSetCommandByName, authenticationHook) 32 | } 33 | -------------------------------------------------------------------------------- /internal/core/common_config/utils.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package common_config 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/environment" 10 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/flags" 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 12 | ) 13 | 14 | // getOverwriteConfig returns a boolean value based on whether the -o flag or the EDGEX_OVERWRITE_CONFIG environment variable is set 15 | func getOverwriteConfig(f *flags.Default, lc logger.LoggingClient) bool { 16 | overwrite := f.OverwriteConfig() 17 | 18 | if b, ok := environment.OverwriteConfig(lc); ok { 19 | overwrite = b 20 | } 21 | 22 | return overwrite 23 | } 24 | -------------------------------------------------------------------------------- /internal/core/common_config/utils_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package common_config 7 | 8 | import ( 9 | "os" 10 | "testing" 11 | 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/flags" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 14 | 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | func TestGetOverwriteConfig(t *testing.T) { 19 | lc := logger.NewMockClient() 20 | 21 | tests := []struct { 22 | name string 23 | flags []string 24 | hasEnvVar bool 25 | envValue string 26 | expectedResult bool 27 | }{ 28 | {"Overwrite config with -o flag", []string{"-o"}, false, "", true}, 29 | {"Overwrite config with EDGEX_OVERWRITE_CONFIG env variable is true", []string{}, true, "true", true}, 30 | {"Not overwrite config with -o flag and EDGEX_OVERWRITE_CONFIG env variable is false", []string{"-o"}, true, "false", false}, 31 | {"Not overwrite config no -o flag and no EDGEX_OVERWRITE_CONFIG env variable defined", []string{}, false, "", false}, 32 | } 33 | 34 | for _, testCase := range tests { 35 | t.Run(testCase.name, func(t *testing.T) { 36 | f := flags.New() 37 | args := testCase.flags 38 | f.Parse(args) 39 | 40 | if testCase.hasEnvVar { 41 | err := os.Setenv("EDGEX_OVERWRITE_CONFIG", testCase.envValue) 42 | require.NoError(t, err) 43 | } 44 | 45 | overwriteConfig := getOverwriteConfig(f, lc) 46 | require.Equal(t, testCase.expectedResult, overwriteConfig) 47 | 48 | err := os.Unsetenv("EDGEX_OVERWRITE_CONFIG") 49 | require.NoError(t, err) 50 | }) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/core/data/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************** 2 | * Copyright 2019 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package container 16 | 17 | import ( 18 | "github.com/edgexfoundry/edgex-go/internal/core/data/config" 19 | 20 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 21 | ) 22 | 23 | // ConfigurationName contains the name of data's config.ConfigurationStruct implementation in the DIC. 24 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 25 | 26 | // ConfigurationFrom helper function queries the DIC and returns datas's config.ConfigurationStruct implementation. 27 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 28 | return get(ConfigurationName).(*config.ConfigurationStruct) 29 | } 30 | -------------------------------------------------------------------------------- /internal/core/data/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/data/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/data/container/device.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/pkg/cache" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DeviceStoreInterfaceName contains the name of the cache.ActiveDeviceStore implementation in the DIC. 15 | var DeviceStoreInterfaceName = di.TypeInstanceToName((*cache.ActiveDeviceStore)(nil)) 16 | 17 | // DeviceStoreFrom helper function queries the DIC and returns the cache.ActiveDeviceStore implementation. 18 | func DeviceStoreFrom(get di.Get) cache.ActiveDeviceStore { 19 | return get(DeviceStoreInterfaceName).(cache.ActiveDeviceStore) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/data/controller/http/const.go: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2025 IOTech Ltd 2 | 3 | package http 4 | 5 | const ( 6 | minOffset = -1 // allow using -1 to query reading data and skip the total count for pagination 7 | ) 8 | -------------------------------------------------------------------------------- /internal/core/data/controller/http/const_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package http 7 | 8 | const ( 9 | ExampleUUID = "82eb2e26-0f24-48aa-ae4c-de9dac3fb9bc" 10 | TestServiceName = "TestService" 11 | TestDeviceName = "TestDevice" 12 | TestProfileName = "TestProfileName" 13 | TestCreatedTime = 1600666214495 14 | TestOriginTime = 1600666185705354000 15 | TestDeviceResourceName = "TestDeviceResourceName" 16 | TestDeviceProfileName = "TestDeviceProfileName" 17 | TestSourceName = "TestSourceName" 18 | 19 | TestReadingValue = "45" 20 | TestBinaryReadingMediaType = "File" 21 | TestReadingBinaryValue = "testbinarydata" 22 | 23 | NonexistentEventID = "8ad33474-fbc5-11ea-adc1-0242ac120002" 24 | ) 25 | -------------------------------------------------------------------------------- /internal/core/data/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "core_data" 9 | -------------------------------------------------------------------------------- /internal/core/data/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/core/data/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- schema for core-data related tables 7 | CREATE SCHEMA IF NOT EXISTS core_data; 8 | -------------------------------------------------------------------------------- /internal/core/data/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024-2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- core_data.device_info is used to store the device related information reusing in the event and reading 7 | CREATE TABLE IF NOT EXISTS core_data.device_info ( 8 | id SERIAL PRIMARY KEY, 9 | devicename TEXT, 10 | profilename TEXT, 11 | sourcename TEXT, 12 | tags JSONB, 13 | resourcename TEXT, 14 | valuetype TEXT DEFAULT '', 15 | units TEXT DEFAULT '', 16 | mediatype TEXT DEFAULT '' 17 | ); 18 | 19 | -- core_data.event is used to store the event information 20 | CREATE TABLE IF NOT EXISTS core_data.event ( 21 | id UUID, 22 | origin BIGINT, 23 | device_info_id SERIAL 24 | ); 25 | 26 | CREATE INDEX IF NOT EXISTS idx_event_origin 27 | ON core_data.event(origin); 28 | 29 | -- core_data.reading is used to store the reading information 30 | CREATE TABLE IF NOT EXISTS core_data.reading ( 31 | event_id UUID, 32 | device_info_id SERIAL, 33 | origin BIGINT, 34 | value TEXT, 35 | binaryvalue BYTEA, 36 | objectvalue JSONB 37 | ); 38 | 39 | CREATE INDEX IF NOT EXISTS idx_reading_origin 40 | ON core_data.reading(origin); 41 | -------------------------------------------------------------------------------- /internal/core/data/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/core/data/mocks/container.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package mocks 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/data/config" 10 | dataContainer "github.com/edgexfoundry/edgex-go/internal/core/data/container" 11 | "github.com/edgexfoundry/go-mod-messaging/v4/messaging/mocks" 12 | "github.com/stretchr/testify/mock" 13 | 14 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 15 | bootstrapConfig "github.com/edgexfoundry/go-mod-bootstrap/v4/config" 16 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 17 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 18 | ) 19 | 20 | // NewMockDIC function returns a mock bootstrap di Container 21 | func NewMockDIC() *di.Container { 22 | msgClient := &mocks.MessageClient{} 23 | msgClient.On("PublishWithSizeLimit", mock.Anything, mock.Anything, mock.Anything).Return(nil) 24 | 25 | return di.NewContainer(di.ServiceConstructorMap{ 26 | dataContainer.ConfigurationName: func(get di.Get) interface{} { 27 | return &config.ConfigurationStruct{ 28 | Writable: config.WritableInfo{ 29 | PersistData: true, 30 | }, 31 | Service: bootstrapConfig.ServiceInfo{ 32 | MaxResultCount: 20, 33 | }, 34 | } 35 | }, 36 | container.LoggingClientInterfaceName: func(get di.Get) interface{} { 37 | return logger.NewMockClient() 38 | }, 39 | container.MessagingClientName: func(get di.Get) interface{} { 40 | return msgClient 41 | }, 42 | }) 43 | } 44 | -------------------------------------------------------------------------------- /internal/core/keeper/constants/const.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package constants 7 | 8 | import ( 9 | "regexp" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 12 | ) 13 | 14 | // new constants relates to EdgeX Keeper service and will be added to go-mod-core-contracts in the future 15 | const CoreKeeperServiceKey = "core-keeper" 16 | 17 | // KeyAllowedCharsRegexString defined the characters allowed in the key name 18 | const KeyAllowedCharsRegexString = "^[a-zA-Z0-9-_~;=./]+$" 19 | 20 | var ( 21 | KeyAllowedCharsRegex = regexp.MustCompile(KeyAllowedCharsRegexString) 22 | ) 23 | 24 | // key delimiter for edgex keeper 25 | const KeyDelimiter = "/" 26 | 27 | // Constants related to defined routes in the v3 service APIs 28 | const ApiKVRoute = common.ApiBase + "/kvs/" + Key + "/{" + Key + ":.*}" 29 | const ApiRegisterRoute = common.ApiBase + "/registry" 30 | const ApiAllRegistrationsRoute = ApiRegisterRoute + "/" + common.All 31 | const ApiRegistrationByServiceIdRoute = ApiRegisterRoute + "/" + ServiceId + "/{" + ServiceId + "}" 32 | 33 | // Constants related to defined url path names and parameters in the v2 service APIs 34 | const ( 35 | Flatten = "flatten" 36 | Key = "key" 37 | KeyOnly = "keyOnly" 38 | Plaintext = "plaintext" 39 | PrefixMatch = "prefixMatch" 40 | ServiceId = "serviceId" 41 | Deregistered = "deregistered" 42 | ) 43 | -------------------------------------------------------------------------------- /internal/core/keeper/container/config.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/keeper/config" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // ConfigurationName contains the name of keeper's config.ConfigurationStruct implementation in the DIC. 15 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 16 | 17 | // ConfigurationFrom helper function queries the DIC and returns keeper's config.ConfigurationStruct implementation. 18 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 19 | return get(ConfigurationName).(*config.ConfigurationStruct) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/keeper/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/keeper/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/keeper/container/registry.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/keeper/infrastructure/interfaces" 10 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 11 | ) 12 | 13 | // RegistryInterfaceName contains the name of the interfaces.Registry implementation in the DIC. 14 | var RegistryInterfaceName = di.TypeInstanceToName((*interfaces.Registry)(nil)) 15 | 16 | // RegistryFrom helper function queries the DIC and returns the interfaces.Registry implementation. 17 | func RegistryFrom(get di.Get) interfaces.Registry { 18 | return get(RegistryInterfaceName).(interfaces.Registry) 19 | } 20 | -------------------------------------------------------------------------------- /internal/core/keeper/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "core_keeper" 9 | -------------------------------------------------------------------------------- /internal/core/keeper/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/core/keeper/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- uuid-ossp extension is used for generating UUID automatically 7 | CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; 8 | 9 | -- schema for core-keeper related tables 10 | CREATE SCHEMA IF NOT EXISTS core_keeper; 11 | -------------------------------------------------------------------------------- /internal/core/keeper/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- core_keeper.config is used to store the config information 7 | CREATE TABLE IF NOT EXISTS core_keeper.config ( 8 | id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), 9 | key TEXT NOT NULL, 10 | value TEXT NOT NULL, 11 | created timestamp NOT NULL DEFAULT (now() AT TIME ZONE 'utc'), 12 | modified timestamp NOT NULL DEFAULT (now() AT TIME ZONE 'utc') 13 | ); 14 | 15 | -- core_keeper.registry is used to store the registry information 16 | CREATE TABLE IF NOT EXISTS core_keeper.registry ( 17 | id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), 18 | content jsonb NOT NULL 19 | ); 20 | -------------------------------------------------------------------------------- /internal/core/keeper/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/core/keeper/infrastructure/interfaces/db.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 10 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 11 | ) 12 | 13 | type DBClient interface { 14 | KeeperKeys(key string, keyOnly bool, isRaw bool) ([]models.KVResponse, errors.EdgeX) 15 | AddKeeperKeys(kv models.KVS, isFlatten bool) ([]models.KeyOnly, errors.EdgeX) 16 | DeleteKeeperKeys(key string, isRecurse bool) ([]models.KeyOnly, errors.EdgeX) 17 | 18 | AddRegistration(r models.Registration) (models.Registration, errors.EdgeX) 19 | DeleteRegistrationByServiceId(id string) errors.EdgeX 20 | Registrations() ([]models.Registration, errors.EdgeX) 21 | RegistrationByServiceId(id string) (models.Registration, errors.EdgeX) 22 | UpdateRegistration(r models.Registration) errors.EdgeX 23 | } 24 | -------------------------------------------------------------------------------- /internal/core/keeper/infrastructure/interfaces/mocks/Registry.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.30.1. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | models "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 7 | mock "github.com/stretchr/testify/mock" 8 | ) 9 | 10 | // Registry is an autogenerated mock type for the Registry type 11 | type Registry struct { 12 | mock.Mock 13 | } 14 | 15 | // DeregisterByServiceId provides a mock function with given fields: id 16 | func (_m *Registry) DeregisterByServiceId(id string) { 17 | _m.Called(id) 18 | } 19 | 20 | // Register provides a mock function with given fields: r 21 | func (_m *Registry) Register(r models.Registration) { 22 | _m.Called(r) 23 | } 24 | 25 | // NewRegistry creates a new instance of Registry. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 26 | // The first argument is typically a *testing.T value. 27 | func NewRegistry(t interface { 28 | mock.TestingT 29 | Cleanup(func()) 30 | }) *Registry { 31 | mock := &Registry{} 32 | mock.Mock.Test(t) 33 | 34 | t.Cleanup(func() { mock.AssertExpectations(t) }) 35 | 36 | return mock 37 | } 38 | -------------------------------------------------------------------------------- /internal/core/keeper/infrastructure/interfaces/registry.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | import "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 9 | 10 | // Registry defines the functionalities of a registry service 11 | type Registry interface { 12 | // Register registers a service with the registration information, 13 | // and health check its status periodically 14 | Register(r models.Registration) 15 | // DeregisterByServiceId de-registers a service by its id and stops 16 | // health checking its status 17 | DeregisterByServiceId(id string) 18 | } 19 | -------------------------------------------------------------------------------- /internal/core/keeper/init.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package keeper 7 | 8 | import ( 9 | "context" 10 | "sync" 11 | 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/startup" 13 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 14 | 15 | "github.com/labstack/echo/v4" 16 | ) 17 | 18 | // Bootstrap contains references to dependencies required by the BootstrapHandler. 19 | type Bootstrap struct { 20 | router *echo.Echo 21 | serviceName string 22 | } 23 | 24 | // NewBootstrap is a factory method that returns an initialized Bootstrap receiver struct. 25 | func NewBootstrap(router *echo.Echo, serviceName string) *Bootstrap { 26 | return &Bootstrap{ 27 | router: router, 28 | serviceName: serviceName, 29 | } 30 | } 31 | 32 | // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the command service. 33 | func (b *Bootstrap) BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { 34 | LoadRestRoutes(b.router, dic, b.serviceName) 35 | 36 | return true 37 | } 38 | -------------------------------------------------------------------------------- /internal/core/keeper/registry/bootstrap.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package registry 7 | 8 | import ( 9 | "context" 10 | "sync" 11 | 12 | "github.com/edgexfoundry/edgex-go/internal/core/keeper/container" 13 | 14 | bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 15 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/startup" 16 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 17 | 18 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 19 | ) 20 | 21 | func BootstrapHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { 22 | dbClient := container.DBClientFrom(dic.Get) 23 | lc := bootstrapContainer.LoggingClientFrom(dic.Get) 24 | 25 | existedRegistrations, err := dbClient.Registrations() 26 | if err != nil { 27 | lc.Errorf("Failed to get registrations from database: %s", err.Error()) 28 | return false 29 | } 30 | 31 | c := NewRegistry(ctx, wg, dic) 32 | for _, r := range existedRegistrations { 33 | if r.Status != models.Halt { 34 | c.Register(r) 35 | } 36 | } 37 | 38 | dic.Update(di.ServiceConstructorMap{ 39 | container.RegistryInterfaceName: func(get di.Get) interface{} { 40 | return c 41 | }, 42 | }) 43 | 44 | return true 45 | } 46 | -------------------------------------------------------------------------------- /internal/core/keeper/router.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package keeper 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go" 10 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/controller" 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/handlers" 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 14 | 15 | keeperController "github.com/edgexfoundry/edgex-go/internal/core/keeper/controller/http" 16 | 17 | "github.com/labstack/echo/v4" 18 | ) 19 | 20 | func LoadRestRoutes(r *echo.Echo, dic *di.Container, serviceName string) { 21 | authenticationHook := handlers.AutoConfigAuthenticationFunc(dic) 22 | 23 | // Common 24 | _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) 25 | 26 | // KV 27 | kv := keeperController.NewKVController(dic) 28 | r.GET(common.ApiKVSByKeyRoute, kv.Keys, authenticationHook) 29 | r.PUT(common.ApiKVSByKeyRoute, kv.AddKeys, authenticationHook) 30 | r.DELETE(common.ApiKVSByKeyRoute, kv.DeleteKeys, authenticationHook) 31 | 32 | // Registry 33 | rc := keeperController.NewRegistryController(dic) 34 | r.POST(common.ApiRegisterRoute, rc.Register, authenticationHook) 35 | r.PUT(common.ApiRegisterRoute, rc.UpdateRegister, authenticationHook) 36 | r.GET(common.ApiAllRegistrationsRoute, rc.Registrations, authenticationHook) 37 | r.GET(common.ApiRegistrationByServiceIdRoute, rc.RegistrationByServiceId, authenticationHook) 38 | r.DELETE(common.ApiRegistrationByServiceIdRoute, rc.Deregister, authenticationHook) 39 | } 40 | -------------------------------------------------------------------------------- /internal/core/keeper/utils/key.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/keeper/constants" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 12 | ) 13 | 14 | // ValidateKeys validates if the key contains invalid characters 15 | func ValidateKeys(key string) errors.EdgeX { 16 | if !constants.KeyAllowedCharsRegex.MatchString(key) { 17 | return errors.NewCommonEdgeX(errors.KindContractInvalid, "key only allows characters between a to z, A to Z, 0 to 9, or one of -_ ~;=./", nil) 18 | } 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/metadata/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package container 16 | 17 | import ( 18 | "github.com/edgexfoundry/edgex-go/internal/core/metadata/config" 19 | 20 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 21 | ) 22 | 23 | // ConfigurationName contains the name of the metadata's config.ConfigurationStruct implementation in the DIC. 24 | var ConfigurationName = di.TypeInstanceToName((*config.ConfigurationStruct)(nil)) 25 | 26 | // ConfigurationFrom helper function queries the DIC and returns metadata's config.ConfigurationStruct implementation. 27 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 28 | return get(ConfigurationName).(*config.ConfigurationStruct) 29 | } 30 | -------------------------------------------------------------------------------- /internal/core/metadata/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/metadata/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/metadata/container/lock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/core/metadata/utils" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // CapacityCheckLockName contains the name of the metadata's utils.CapacityCheckLock implementation in the DIC. 15 | var CapacityCheckLockName = di.TypeInstanceToName((*utils.CapacityCheckLock)(nil)) 16 | 17 | // CapacityCheckLockFrom helper function queries the DIC and returns metadata's utils.CapacityCheckLock implementation. 18 | func CapacityCheckLockFrom(get di.Get) *utils.CapacityCheckLock { 19 | return get(CapacityCheckLockName).(*utils.CapacityCheckLock) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/metadata/container/uom.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 10 | 11 | "github.com/edgexfoundry/edgex-go/internal/core/metadata/infrastructure/interfaces" 12 | ) 13 | 14 | // UnitsOfMeasureInterfaceName contains the name of the interfaces.UnitsOfMeasure implementation in the DIC. 15 | var UnitsOfMeasureInterfaceName = di.TypeInstanceToName((*interfaces.UnitsOfMeasure)(nil)) 16 | 17 | // UnitsOfMeasureFrom helper function queries the DIC and returns the interfaces.UnitsOfMeasure implementation. 18 | func UnitsOfMeasureFrom(get di.Get) interfaces.UnitsOfMeasure { 19 | return get(UnitsOfMeasureInterfaceName).(interfaces.UnitsOfMeasure) 20 | } 21 | -------------------------------------------------------------------------------- /internal/core/metadata/controller/http/const_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020-2023 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package http 7 | 8 | const ( 9 | ExampleUUID = "82eb2e26-0f24-48aa-ae4c-de9dac3fb9bc" 10 | TestDeviceProfileName = "TestDeviceProfileName" 11 | TestManufacturer = "TestManufacturer" 12 | TestDescription = "TestDescription" 13 | TestModel = "TestModel" 14 | TestDeviceResourceName = "TestDeviceResourceName" 15 | TestUnits = "TestUnits" 16 | TestDeviceCommandName = "TestDeviceCommand" 17 | TestDeviceName = "TestDevice" 18 | TestDeviceServiceName = "TestDeviceServiceName" 19 | ) 20 | -------------------------------------------------------------------------------- /internal/core/metadata/controller/http/uom.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022-2023 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package http 7 | 8 | import ( 9 | "net/http" 10 | 11 | bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 14 | "github.com/edgexfoundry/go-mod-core-contracts/v4/dtos/responses" 15 | 16 | "github.com/edgexfoundry/edgex-go/internal/core/metadata/container" 17 | "github.com/edgexfoundry/edgex-go/internal/pkg" 18 | "github.com/edgexfoundry/edgex-go/internal/pkg/utils" 19 | 20 | "github.com/labstack/echo/v4" 21 | ) 22 | 23 | type UnitOfMeasureController struct { 24 | dic *di.Container 25 | } 26 | 27 | func NewUnitOfMeasureController(dic *di.Container) *UnitOfMeasureController { 28 | return &UnitOfMeasureController{ 29 | dic: dic, 30 | } 31 | } 32 | 33 | func (uc *UnitOfMeasureController) UnitsOfMeasure(c echo.Context) error { 34 | r := c.Request() 35 | w := c.Response() 36 | ctx := r.Context() 37 | u := container.UnitsOfMeasureFrom(uc.dic.Get) 38 | lc := bootstrapContainer.LoggingClientFrom(uc.dic.Get) 39 | 40 | response := responses.NewUnitsOfMeasureResponse("", "", http.StatusOK, u) 41 | 42 | utils.WriteHttpHeader(w, ctx, http.StatusOK) 43 | 44 | switch r.Header.Get(common.Accept) { 45 | case common.ContentTypeYAML: 46 | return pkg.EncodeAndWriteYamlResponse(u, w, lc) 47 | default: 48 | return pkg.EncodeAndWriteResponse(response, w, lc) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/core/metadata/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "core_metadata" 9 | -------------------------------------------------------------------------------- /internal/core/metadata/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/core/metadata/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- schema for core-metadata related tables 7 | CREATE SCHEMA IF NOT EXISTS core_metadata; 8 | -------------------------------------------------------------------------------- /internal/core/metadata/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- core_metadata.device_service is used to store the device_service information 7 | CREATE TABLE IF NOT EXISTS core_metadata.device_service ( 8 | id UUID PRIMARY KEY, 9 | content JSONB NOT NULL 10 | ); 11 | 12 | -- core_metadata.device_profile is used to store the device_profile information 13 | CREATE TABLE IF NOT EXISTS core_metadata.device_profile ( 14 | id UUID PRIMARY KEY, 15 | content JSONB NOT NULL 16 | ); 17 | 18 | -- core_metadata.device is used to store the device information 19 | CREATE TABLE IF NOT EXISTS core_metadata.device ( 20 | id UUID PRIMARY KEY, 21 | content JSONB NOT NULL 22 | ); 23 | 24 | -- core_metadata.provision_watcher is used to store the provision watcher information 25 | CREATE TABLE IF NOT EXISTS core_metadata.provision_watcher ( 26 | id UUID PRIMARY KEY, 27 | content JSONB NOT NULL 28 | ); 29 | -------------------------------------------------------------------------------- /internal/core/metadata/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/core/metadata/infrastructure/interfaces/mocks/UnitsOfMeasure.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.10.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import mock "github.com/stretchr/testify/mock" 6 | 7 | // UnitsOfMeasure is an autogenerated mock type for the UnitsOfMeasure type 8 | type UnitsOfMeasure struct { 9 | mock.Mock 10 | } 11 | 12 | // Validate provides a mock function with given fields: _a0 13 | func (_m *UnitsOfMeasure) Validate(_a0 string) bool { 14 | ret := _m.Called(_a0) 15 | 16 | var r0 bool 17 | if rf, ok := ret.Get(0).(func(string) bool); ok { 18 | r0 = rf(_a0) 19 | } else { 20 | r0 = ret.Get(0).(bool) 21 | } 22 | 23 | return r0 24 | } 25 | -------------------------------------------------------------------------------- /internal/core/metadata/infrastructure/interfaces/uom.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | // UnitsOfMeasure defines required functionality to perform units of measure 9 | // validation in EdgeX 10 | type UnitsOfMeasure interface { 11 | // Validate validates DeviceResource's unit against the list of 12 | // units of measure by core metadata. 13 | Validate(string) bool 14 | } 15 | -------------------------------------------------------------------------------- /internal/core/metadata/uom/uom.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package uom 7 | 8 | type UnitsOfMeasureImpl struct { 9 | Source string `json:"source,omitempty" yaml:"Source,omitempty"` 10 | Units map[string]Unit `json:"units,omitempty" yaml:"Units,omitempty"` 11 | } 12 | 13 | type Unit struct { 14 | Source string `json:"source,omitempty" yaml:"Source,omitempty"` 15 | Values []string `json:"values,omitempty" yaml:"Values,omitempty"` 16 | } 17 | 18 | func (u *UnitsOfMeasureImpl) Validate(unit string) bool { 19 | if unit == "" || len(u.Units) == 0 { 20 | return true 21 | } 22 | 23 | for _, units := range u.Units { 24 | for _, v := range units.Values { 25 | if unit == v { 26 | return true 27 | } 28 | } 29 | } 30 | 31 | return false 32 | } 33 | -------------------------------------------------------------------------------- /internal/core/metadata/utils/lock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import "sync" 9 | 10 | type CapacityCheckLock struct { 11 | mutex sync.RWMutex 12 | } 13 | 14 | func NewCapacityCheckLock() *CapacityCheckLock { 15 | return &CapacityCheckLock{} 16 | } 17 | 18 | func (c *CapacityCheckLock) Lock() { 19 | c.mutex.Lock() 20 | } 21 | func (c *CapacityCheckLock) Unlock() { 22 | c.mutex.Unlock() 23 | } 24 | -------------------------------------------------------------------------------- /internal/interface.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | import "net/http" 4 | 5 | type HttpCaller interface { 6 | Do(req *http.Request) (*http.Response, error) 7 | } 8 | -------------------------------------------------------------------------------- /internal/pkg/bootstrap/interfaces/database.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package interfaces 16 | 17 | import "github.com/edgexfoundry/go-mod-bootstrap/v4/config" 18 | 19 | // Database interface provides an abstraction for obtaining the database configuration information. 20 | type Database interface { 21 | // GetDatabaseInfo returns a database information. 22 | GetDatabaseInfo() config.Database 23 | } 24 | -------------------------------------------------------------------------------- /internal/pkg/common/util.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package common 7 | 8 | import ( 9 | "time" 10 | ) 11 | 12 | func MakeTimestamp() int64 { 13 | return time.Now().UTC().UnixMilli() 14 | } 15 | 16 | // FindCommonStrings finds the common string from multiple string slices 17 | // e.g. 18 | // stringSlice1 = []string{"aaa", "bbb", "ccc"} 19 | // stringSlice2 = []string{"bbb", "ccc", "ddd"} 20 | // stringSlice3 = []string{"aaa", "bbb", "ccc", "eee"} 21 | // FindCommonStrings(stringSlice1, stringSlice2, stringSlice3) shall return a string slice with {"bbb", "ccc"} 22 | func FindCommonStrings(stringsArray ...[]string) (commonStrings []string) { 23 | if len(stringsArray) == 0 { 24 | return nil 25 | } else if len(stringsArray) == 1 { 26 | return stringsArray[0] 27 | } 28 | commonStringsSet := make([]string, 0) 29 | hash := make(map[string]bool) 30 | for _, s := range stringsArray[0] { 31 | hash[s] = true 32 | } 33 | for _, s := range stringsArray[1] { 34 | if _, ok := hash[s]; ok { 35 | commonStringsSet = append(commonStringsSet, s) 36 | } 37 | } 38 | stringsArray = append([][]string{commonStringsSet}, stringsArray[2:]...) 39 | return FindCommonStrings(stringsArray...) 40 | } 41 | 42 | // ConvertStringsToInterfaces converts a string array to an interface{} array 43 | func ConvertStringsToInterfaces(stringArray []string) []interface{} { 44 | result := make([]interface{}, len(stringArray)) 45 | for i, s := range stringArray { 46 | result[i] = s 47 | } 48 | return result 49 | } 50 | -------------------------------------------------------------------------------- /internal/pkg/correlation/handler.go: -------------------------------------------------------------------------------- 1 | package correlation 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/google/uuid" 7 | 8 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 9 | ) 10 | 11 | func FromContext(ctx context.Context) string { 12 | hdr, ok := ctx.Value(common.CorrelationHeader).(string) 13 | if !ok { 14 | hdr = "" 15 | } 16 | return hdr 17 | } 18 | 19 | // FromContextOrNew returns the correlation ID from the context if it exists, otherwise it generates a new one and put it back to the context. 20 | func FromContextOrNew(ctx context.Context) (context.Context, string) { 21 | hdr := FromContext(ctx) 22 | if hdr == "" { 23 | hdr = uuid.New().String() 24 | // lint:ignore SA1029 legacy 25 | // nolint:staticcheck // See golangci-lint #741 26 | ctx = context.WithValue(ctx, common.CorrelationHeader, hdr) 27 | } 28 | return ctx, hdr 29 | } 30 | -------------------------------------------------------------------------------- /internal/pkg/db/db.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2018 Dell Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package db 16 | 17 | import ( 18 | "errors" 19 | ) 20 | 21 | var ( 22 | ErrNotFound = errors.New("Item not found") 23 | ErrUnsupportedDatabase = errors.New("Unsupported database type") 24 | ErrInvalidObjectId = errors.New("Invalid object ID") 25 | ErrNotUnique = errors.New("Resource already exists") 26 | ErrCommandStillInUse = errors.New("Command is still in use by device profiles") 27 | ErrSlugEmpty = errors.New("Slug is nil or empty") 28 | ErrNameEmpty = errors.New("Name is required") 29 | ) 30 | 31 | type Configuration struct { 32 | DbType string 33 | Host string 34 | Port int 35 | Timeout string 36 | DatabaseName string 37 | Username string 38 | Password string 39 | BatchSize int 40 | } 41 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/postgres/client.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package postgres 7 | 8 | import ( 9 | "context" 10 | "embed" 11 | 12 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 14 | 15 | "github.com/edgexfoundry/edgex-go/internal/pkg/db" 16 | postgresClient "github.com/edgexfoundry/edgex-go/internal/pkg/db/postgres" 17 | "github.com/edgexfoundry/edgex-go/internal/pkg/infrastructure/postgres/cache" 18 | ) 19 | 20 | type Client struct { 21 | *postgresClient.Client 22 | loggingClient logger.LoggingClient 23 | deviceInfoIdCache cache.DeviceInfoIdCache 24 | } 25 | 26 | func NewClient(ctx context.Context, config db.Configuration, lc logger.LoggingClient, schemaName, serviceKey, serviceVersion string, sqlFiles embed.FS) (*Client, errors.EdgeX) { 27 | var err error 28 | dc := &Client{} 29 | dc.Client, err = postgresClient.NewClient(ctx, config, lc, schemaName, serviceKey, serviceVersion, sqlFiles) 30 | dc.loggingClient = lc 31 | if err != nil { 32 | return nil, errors.NewCommonEdgeX(errors.KindDatabaseError, "postgres client creation failed", err) 33 | } 34 | dc.deviceInfoIdCache = cache.NewDeviceInfoIdCache(lc) 35 | 36 | return dc, nil 37 | } 38 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/postgres/client_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package postgres 7 | 8 | import ( 9 | dataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/data/infrastructure/interfaces" 10 | metadataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/metadata/infrastructure/interfaces" 11 | notificationsInterfaces "github.com/edgexfoundry/edgex-go/internal/support/notifications/infrastructure/interfaces" 12 | schedulerInterfaces "github.com/edgexfoundry/edgex-go/internal/support/scheduler/infrastructure/interfaces" 13 | ) 14 | 15 | // Check the implementation of Postgres satisfies the DB client 16 | var _ dataInterfaces.DBClient = &Client{} 17 | var _ metadataInterfaces.DBClient = &Client{} 18 | var _ schedulerInterfaces.DBClient = &Client{} 19 | var _ notificationsInterfaces.DBClient = &Client{} 20 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/postgres/models/deviceinfo.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package models 7 | 8 | type DeviceInfo struct { 9 | Id int 10 | DeviceName string 11 | ProfileName string 12 | SourceName string 13 | Tags map[string]any 14 | ResourceName string 15 | ValueType string 16 | Units string 17 | MediaType string 18 | } 19 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/postgres/models/reading.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package models 7 | 8 | import "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 9 | 10 | // Reading struct contains the columns of the core_data.reading table in Postgres db relates to a reading 11 | // which includes all the fields in BaseReading, BinaryReading, SimpleReading and ObjectReading 12 | type Reading struct { 13 | models.BaseReading 14 | EventId string `db:"event_id"` // the foreign key refers to the id column in core_data.event table 15 | BinaryReading 16 | SimpleReading 17 | ObjectReading 18 | } 19 | 20 | type SimpleReading struct { 21 | Value *string 22 | } 23 | 24 | type BinaryReading struct { 25 | BinaryValue []byte 26 | MediaType *string 27 | } 28 | 29 | type ObjectReading struct { 30 | ObjectValue any 31 | } 32 | 33 | // GetBaseReading makes the Reading struct to implement the go-mod-core-contract Reading interface in models 34 | func (r Reading) GetBaseReading() models.BaseReading { 35 | return models.BaseReading{ 36 | Id: r.Id, 37 | Origin: r.Origin, 38 | DeviceName: r.DeviceName, 39 | ResourceName: r.ResourceName, 40 | ProfileName: r.ProfileName, 41 | ValueType: r.ValueType, 42 | Units: r.Units, 43 | Tags: r.Tags, 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/redis/client_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2021 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package redis 7 | 8 | import dataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/data/infrastructure/interfaces" 9 | import metadataInterfaces "github.com/edgexfoundry/edgex-go/internal/core/metadata/infrastructure/interfaces" 10 | import notificationsInterfaces "github.com/edgexfoundry/edgex-go/internal/support/notifications/infrastructure/interfaces" 11 | 12 | // Check the implementation of Redis satisfies the DB client 13 | var _ dataInterfaces.DBClient = &Client{} 14 | var _ metadataInterfaces.DBClient = &Client{} 15 | var _ notificationsInterfaces.DBClient = &Client{} 16 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/redis/dbconsts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package redis 7 | 8 | // Redis commmands used in this project 9 | // Reference: https://redis.io/commands 10 | const ( 11 | MULTI = "MULTI" 12 | SET = "SET" 13 | GET = "GET" 14 | EXISTS = "EXISTS" 15 | DEL = "DEL" 16 | HGETALL = "HGETALL" 17 | HLEN = "HLEN" 18 | HSET = "HSET" 19 | HSETNX = "HSETNX" 20 | HGET = "HGET" 21 | HEXISTS = "HEXISTS" 22 | HDEL = "HDEL" 23 | SADD = "SADD" 24 | SREM = "SREM" 25 | ZADD = "ZADD" 26 | ZREM = "ZREM" 27 | EXEC = "EXEC" 28 | ZRANGE = "ZRANGE" 29 | ZREVRANGE = "ZREVRANGE" 30 | MGET = "MGET" 31 | ZCARD = "ZCARD" 32 | ZCOUNT = "ZCOUNT" 33 | UNLINK = "UNLINK" 34 | ZRANGEBYSCORE = "ZRANGEBYSCORE" 35 | ZREVRANGEBYSCORE = "ZREVRANGEBYSCORE" 36 | LIMIT = "LIMIT" 37 | ZUNIONSTORE = "ZUNIONSTORE" 38 | ZINTERSTORE = "ZINTERSTORE" 39 | TYPE = "TYPE" 40 | INFO = "INFO" 41 | MEMORY = "MEMORY" 42 | WEIGHTS = "WEIGHTS" 43 | ) 44 | 45 | const ( 46 | InfiniteMin = "-inf" 47 | InfiniteMax = "+inf" 48 | GreaterThanZero = "(0" 49 | DBKeySeparator = ":" 50 | ) 51 | 52 | // Redis data types 53 | const ( 54 | Hash = "hash" 55 | String = "string" 56 | None = "none" 57 | ) 58 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/redis/dbhelper.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package redis 7 | 8 | import "strings" 9 | 10 | // CreateKey creates Redis key by connecting the target key with DBKeySeparator 11 | func CreateKey(targets ...string) string { 12 | return strings.Join(targets, DBKeySeparator) 13 | } 14 | -------------------------------------------------------------------------------- /internal/pkg/infrastructure/redis/dbhelper_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package redis 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestCreateKey(t *testing.T) { 15 | result := CreateKey(EventsCollectionDeviceName, "TestDeviceName") 16 | expected := EventsCollectionDeviceName + DBKeySeparator + "TestDeviceName" 17 | assert.Equal(t, expected, result) 18 | } 19 | -------------------------------------------------------------------------------- /internal/pkg/interfaces/db.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | type DBClient interface { 9 | CloseSession() 10 | } 11 | -------------------------------------------------------------------------------- /internal/pkg/utils/count.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 12 | ) 13 | 14 | // CheckCountRange evaluates if the offset and limit parameters are within the valid range. 15 | func CheckCountRange(totalCount uint32, offset, limit int) (continueExec bool, err errors.EdgeX) { 16 | if limit == 0 || totalCount == 0 { 17 | return false, nil 18 | } 19 | if offset > int(totalCount) { 20 | return false, errors.NewCommonEdgeX(errors.KindRangeNotSatisfiable, fmt.Sprintf("query objects bounds out of range. length:%v offset:%v", totalCount, offset), nil) 21 | } 22 | 23 | return true, nil 24 | } 25 | -------------------------------------------------------------------------------- /internal/pkg/utils/count_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "github.com/stretchr/testify/require" 10 | "testing" 11 | ) 12 | 13 | func TestCheckCountRange(t *testing.T) { 14 | count := uint32(1) 15 | tests := []struct { 16 | name string 17 | totalCount uint32 18 | offset int 19 | limit int 20 | continueExec bool 21 | expectErr bool 22 | }{ 23 | {"valid - total count is zero ", uint32(0), 0, 0, false, false}, 24 | {"valid - limit is zero ", count, 0, 0, false, false}, 25 | {"valid - valid range", count, 0, 1, true, false}, 26 | {"invalid - offset out of range", count, 2, 1, false, true}, 27 | } 28 | for _, tt := range tests { 29 | t.Run(tt.name, func(t *testing.T) { 30 | cont, err := CheckCountRange(tt.totalCount, tt.offset, tt.limit) 31 | require.Equal(t, tt.continueExec, cont) 32 | if tt.continueExec { 33 | require.NoError(t, err) 34 | } 35 | if tt.expectErr { 36 | require.Error(t, err) 37 | } 38 | }) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/pkg/utils/crypto/aes_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package crypto 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestAESCryptor_Encryption(t *testing.T) { 15 | testData := "test data" 16 | aesCryptor := NewAESCryptor() 17 | 18 | encrypted, err := aesCryptor.Encrypt(testData) 19 | require.NoError(t, err) 20 | 21 | decrypted, err := aesCryptor.Decrypt(encrypted) 22 | require.NoError(t, err) 23 | require.Equal(t, testData, string(decrypted)) 24 | } 25 | -------------------------------------------------------------------------------- /internal/pkg/utils/crypto/interfaces/crypto.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | import "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 9 | 10 | type Crypto interface { 11 | Encrypt(string) (string, errors.EdgeX) 12 | Decrypt(string) ([]byte, errors.EdgeX) 13 | } 14 | -------------------------------------------------------------------------------- /internal/pkg/utils/struct.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "reflect" 10 | ) 11 | 12 | func OnlyOneFieldUpdated(fieldName string, model interface{}) bool { 13 | res := true 14 | 15 | t := reflect.TypeOf(model) 16 | v := reflect.ValueOf(model) 17 | for i := 0; i < t.NumField(); i++ { 18 | f := t.Field(i) 19 | if f.Name != fieldName && f.Name != "Name" && f.Name != "Id" { 20 | if !v.FieldByName(f.Name).IsNil() { 21 | res = false 22 | break 23 | } 24 | } 25 | } 26 | 27 | return res 28 | } 29 | -------------------------------------------------------------------------------- /internal/pkg/utils/struct_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2022 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/dtos" 12 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestOnlyOneFieldUpdated(t *testing.T) { 17 | testService := "test-service" 18 | testDescription := "test-description" 19 | testAdminState := models.Locked 20 | oneUpdated := dtos.UpdateDeviceService{ 21 | Name: &testService, 22 | Description: &testDescription, 23 | } 24 | twoUpdated := oneUpdated 25 | twoUpdated.AdminState = &testAdminState 26 | 27 | tests := []struct { 28 | name string 29 | fieldName string 30 | model interface{} 31 | expected bool 32 | }{ 33 | {"valid", "Description", oneUpdated, true}, 34 | {"invalid - two fields are updated", "Description", twoUpdated, false}, 35 | } 36 | for _, tt := range tests { 37 | t.Run(tt.name, func(t *testing.T) { 38 | assert.Equal(t, tt.expected, OnlyOneFieldUpdated(tt.fieldName, tt.model)) 39 | }) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /internal/pkg/utils/time.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2023 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "time" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 12 | ) 13 | 14 | // CheckMinInterval parses the ISO 8601 time duration string to Duration type 15 | // and evaluates if the duration value is smaller than the suggested minimum duration 16 | func CheckMinInterval(value string, minDuration time.Duration, lc logger.LoggingClient) { 17 | valueDuration, err := time.ParseDuration(value) 18 | if err != nil { 19 | lc.Errorf("failed to parse the interval duration string %s to a duration time value: %v", value, err) 20 | return 21 | } 22 | 23 | if valueDuration < minDuration { 24 | // the duration value is smaller than the min 25 | lc.Warnf("the interval value '%s' is smaller than the suggested value '%s', which might cause abnormal CPU increase", value, minDuration) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/README.md: -------------------------------------------------------------------------------- 1 | # Notes For Adding A New Broker Type To Message Bus 2 | 3 | In order to add a new message bus broker type create a new folder and implement specific handler and add to messagebus_factory.go similar to the following code snippet 4 | ```go 5 | case Mosquitto: 6 | mosquitto.Configure(ctx, cancel, f) 7 | return nil 8 | ``` 9 | 10 | Add a new compose file snippet for new broker type similar to the following example for Mosquitto Broker 11 | ```yml 12 | version: '3.7' 13 | 14 | volumes: 15 | mqtt: 16 | 17 | services: 18 | mqtt-broker: 19 | image: eclipse-mosquitto:${MOSQUITTO_VERSION} 20 | entrypoint: ["/edgex-init/messagebus_wait_install.sh"] 21 | env_file: 22 | - common-security.env 23 | - common-sec-stage-gate.env 24 | environment: 25 | BROKER_TYPE: mosquitto 26 | CONF_DIR: /edgex-init/bootstrap-mqtt/res 27 | ENTRYPOINT_ARG: /usr/sbin/mosquitto -c /mosquitto/config/mosquitto.conf 28 | ports: 29 | - "127.0.0.1:1883:1883" 30 | volumes: 31 | - mqtt:/mosquitto:z 32 | - edgex-init:/edgex-init:ro,z 33 | - /tmp/edgex/secrets/security-bootstrapper-messagebus:/tmp/edgex/secrets/security-bootstrapper-messagebus:ro,z 34 | depends_on: 35 | - security-bootstrapper 36 | - secretstore-setup 37 | container_name: edgex-mqtt-broker 38 | hostname: edgex-mqtt-broker 39 | read_only: true 40 | restart: always 41 | networks: 42 | - edgex-network 43 | security_opt: 44 | - no-new-privileges:true 45 | # root privilege required for bootstrapper's process 46 | user: root:root 47 | ``` -------------------------------------------------------------------------------- /internal/security/bootstrapper/command/help/command_test.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package help 17 | 18 | import ( 19 | "testing" 20 | 21 | "github.com/stretchr/testify/require" 22 | 23 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/config" 24 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/interfaces" 25 | 26 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 27 | ) 28 | 29 | // TestHelp tests functionality of help command 30 | func TestHelp(t *testing.T) { 31 | // Arrange 32 | lc := logger.MockLogger{} 33 | config := &config.ConfigurationStruct{} 34 | 35 | // Act 36 | command, err := NewCommand(lc, config, []string{}) 37 | require.NoError(t, err) 38 | 39 | code, err := command.Execute() 40 | 41 | // Assert 42 | require.NoError(t, err) 43 | require.Equal(t, interfaces.StatusCodeExitNormal, code) 44 | } 45 | 46 | // TestHelpBadArg tests unknown arg handler 47 | func TestHelpBadArg(t *testing.T) { 48 | // Arrange 49 | lc := logger.MockLogger{} 50 | config := &config.ConfigurationStruct{} 51 | 52 | // Act 53 | command, err := NewCommand(lc, config, []string{"-badarg"}) 54 | 55 | // Assert 56 | require.Error(t, err) 57 | require.Nil(t, command) 58 | } 59 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/command/waitfor/uri_flags.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package waitfor 17 | 18 | import "fmt" 19 | 20 | type uriFlagsVar []string 21 | 22 | // String overrides the Flag.Value interface, method String() string 23 | func (uris *uriFlagsVar) String() string { 24 | return fmt.Sprint(*uris) 25 | } 26 | 27 | // Set overrides the Flag.Value interface, method Set(string) error 28 | // uriFlagsVar is a slice of string flags and thus we aggregate it over each flag call 29 | func (uris *uriFlagsVar) Set(value string) error { 30 | *uris = append(*uris, value) 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/config/waitFor.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package config 17 | 18 | // WaitForInfo defines some fields related to 19 | // waitFor subcommand of security-bootstrapper 20 | type WaitForInfo struct { 21 | Timeout string 22 | RetryInterval string 23 | } 24 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/container/container.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package container 17 | 18 | import ( 19 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/config" 20 | 21 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 22 | ) 23 | 24 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 25 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 26 | 27 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 28 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 29 | return get(ConfigurationName).(*config.ConfigurationStruct) 30 | } 31 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/interfaces/command.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package interfaces 17 | 18 | const ( 19 | // StatusCodeExitNormal exit code for normal case 20 | StatusCodeExitNormal = 0 21 | // StatusCodeNoOptionSelected exit code for missing options case 22 | StatusCodeNoOptionSelected = 1 23 | // StatusCodeExitWithError is exit code for error case 24 | StatusCodeExitWithError = 2 25 | ) 26 | 27 | // Command implement the Command pattern 28 | type Command interface { 29 | Execute() (statusCode int, err error) 30 | GetCommandName() string 31 | } 32 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/messagebus_factory.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2022 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package bootstrapper 17 | 18 | import ( 19 | "context" 20 | "fmt" 21 | 22 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/mosquitto" 23 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/flags" 24 | ) 25 | 26 | const ( 27 | Mosquitto = "mosquitto" 28 | Redis = "redis" 29 | ) 30 | 31 | func ConfigureSecureMessageBus(brokerType string, ctx context.Context, cancel context.CancelFunc, f flags.Common) error { 32 | switch brokerType { 33 | case Mosquitto: 34 | mosquitto.Configure(ctx, cancel, f) 35 | return nil 36 | case Redis: 37 | //no op as Redis message bus is handled in configureRedis 38 | return nil 39 | default: 40 | return fmt.Errorf("Broker Type Not Supported: %s", brokerType) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/mosquitto/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2022 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package container 16 | 17 | import ( 18 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/mosquitto/config" 19 | 20 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 21 | ) 22 | 23 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 24 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 25 | 26 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 27 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 28 | return get(ConfigurationName).(*config.ConfigurationStruct) 29 | } 30 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/postgres/container/config.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/postgres/config" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 15 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 16 | 17 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 18 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 19 | return get(ConfigurationName).(*config.ConfigurationStruct) 20 | } 21 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/redis/Developer-notes.md: -------------------------------------------------------------------------------- 1 | # Notes for developers regarding to use different Redis Access Control List (ACL) 2 | 3 | Currently, the `security-bootstrapper` configureRedis produces the ACL configuration file for Redis' default user. 4 | Should using different ACL rules call for a debugging needs, developers could override this built-in configuration behavior as follows: 5 | 6 | Currently, the default ACL file path inside the redis.conf is pointing to the path with the file name `edgex_redis_acl.conf`. A developer can always provide his own redis config file containing the different file name (eg. developer-acl.conf) for ACL rules like adding some `dangerous` commands such as `INFO, MONITOR, BGSAVE, and FLUSHD` inside his own ACL file using `+` directive. eg.: 7 | 8 | ```text 9 | user default on allkeys +@all -@dangerous #_{{.HashedRedisPwd}}_ +INFO +MONITOR +BGSAVE + FLUSHDB 10 | ``` 11 | 12 | and use his own config file on developer modified redis' entrypoint script to start the redis server like: 13 | 14 | ```sh 15 | exec /usr/local/bin/docker-entrypoint.sh redis-server developer_redis.conf 16 | ``` 17 | 18 | on `database` service of a docker-compose file. 19 | 20 | Note that the HashedRedisPwd still needs to be come from the original dynamically created redis.conf file as it is read from secretstore Vault. 21 | 22 | A developer can also just modified the ACL file `edgex_redis_acl.conf` directly and then use `ACL LOAD` or `ACL SAVE` commands to change ACL rules assuming he/she has the right permissions to update that file. 23 | -------------------------------------------------------------------------------- /internal/security/bootstrapper/redis/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | 17 | package container 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/security/bootstrapper/redis/config" 21 | 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | ) 24 | 25 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 26 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 27 | 28 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 29 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 30 | return get(ConfigurationName).(*config.ConfigurationStruct) 31 | } 32 | -------------------------------------------------------------------------------- /internal/security/common/tokenpolicy_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019-2023 Intel Corporation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 4 | // in compliance with the License. You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the License 9 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 10 | // or implied. See the License for the specific language governing permissions and limitations under 11 | // the License. 12 | // 13 | // SPDX-License-Identifier: Apache-2.0 14 | package common 15 | 16 | import ( 17 | "encoding/json" 18 | "testing" 19 | 20 | "github.com/stretchr/testify/require" 21 | ) 22 | 23 | func TestDefaultTokenPolicy(t *testing.T) { 24 | // Act 25 | policies := MakeDefaultTokenPolicy("service-name") 26 | 27 | // Assert 28 | bytes, err := json.Marshal(policies) 29 | require.NoError(t, err) 30 | require.NotEmpty(t, bytes) 31 | 32 | expected := map[string]interface{}{ 33 | "path": map[string]interface{}{ 34 | "secret/edgex/service-name/*": map[string]interface{}{ 35 | "capabilities": []string{"create", "update", "delete", "list", "read"}, 36 | }, 37 | "identity/oidc/token/service-name": map[string]interface{}{ 38 | "capabilities": []string{"read"}, 39 | }, 40 | "identity/oidc/introspect": map[string]interface{}{ 41 | "capabilities": []string{"create", "update"}, 42 | }, 43 | }, 44 | } 45 | 46 | require.Equal(t, expected, policies) 47 | } 48 | -------------------------------------------------------------------------------- /internal/security/common/usermanager_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package common 8 | -------------------------------------------------------------------------------- /internal/security/config/command/help/command.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package help 8 | 9 | import ( 10 | "flag" 11 | "fmt" 12 | "strings" 13 | 14 | "github.com/edgexfoundry/edgex-go/internal/security/config/command" 15 | "github.com/edgexfoundry/edgex-go/internal/security/config/interfaces" 16 | 17 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 18 | ) 19 | 20 | const ( 21 | CommandName = "help" 22 | ) 23 | 24 | type cmd struct { 25 | loggingClient logger.LoggingClient 26 | flagSet *flag.FlagSet 27 | } 28 | 29 | func NewCommand( 30 | lc logger.LoggingClient, 31 | args []string) (interfaces.Command, error) { 32 | 33 | flagSet := flag.NewFlagSet(CommandName, flag.ContinueOnError) 34 | err := flagSet.Parse(args) 35 | if err != nil { 36 | return nil, fmt.Errorf("Unable to parse command: %s: %w", strings.Join(args, " "), err) 37 | } 38 | 39 | return &cmd{ 40 | loggingClient: lc, 41 | flagSet: flagSet, 42 | }, nil 43 | } 44 | 45 | func (c *cmd) Execute() (statusCode int, err error) { 46 | command.HelpCallback() 47 | return interfaces.StatusCodeExitNormal, nil 48 | } 49 | -------------------------------------------------------------------------------- /internal/security/config/command/help/command_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package help 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/edgexfoundry/edgex-go/internal/security/config/interfaces" 13 | 14 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 15 | 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | // TestHelp tests functionality of help command 20 | func TestHelp(t *testing.T) { 21 | // Arrange 22 | lc := logger.MockLogger{} 23 | 24 | // Act 25 | command, err := NewCommand(lc, []string{}) 26 | require.NoError(t, err) 27 | 28 | code, err := command.Execute() 29 | 30 | // Assert 31 | require.NoError(t, err) 32 | require.Equal(t, interfaces.StatusCodeExitNormal, code) 33 | } 34 | 35 | // TestHelpBadArg tests unknown arg handler 36 | func TestHelpBadArg(t *testing.T) { 37 | // Arrange 38 | lc := logger.MockLogger{} 39 | 40 | // Act 41 | command, err := NewCommand(lc, []string{"-badarg"}) 42 | 43 | // Assert 44 | require.Error(t, err) 45 | require.Nil(t, command) 46 | } 47 | -------------------------------------------------------------------------------- /internal/security/config/command/proxy/adduser/testdata/token.json: -------------------------------------------------------------------------------- 1 | {"root_token":"abcd"} 2 | -------------------------------------------------------------------------------- /internal/security/config/command/proxy/command.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package proxy 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/edgexfoundry/edgex-go/internal/security/config/command/proxy/adduser" 13 | "github.com/edgexfoundry/edgex-go/internal/security/config/command/proxy/deluser" 14 | "github.com/edgexfoundry/edgex-go/internal/security/config/command/proxy/tls" 15 | "github.com/edgexfoundry/edgex-go/internal/security/config/interfaces" 16 | "github.com/edgexfoundry/edgex-go/internal/security/secretstore/config" 17 | 18 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 19 | ) 20 | 21 | const ( 22 | CommandName = "proxy" 23 | ) 24 | 25 | func NewCommand( 26 | lc logger.LoggingClient, 27 | configuration *config.ConfigurationStruct, 28 | args []string) (interfaces.Command, error) { 29 | 30 | var command interfaces.Command 31 | var err error 32 | 33 | if len(args) < 1 { 34 | return nil, fmt.Errorf("subcommand required (adduser, deluser, tls)") 35 | } 36 | 37 | commandName := args[0] 38 | 39 | switch commandName { 40 | case tls.CommandName: 41 | command, err = tls.NewCommand(lc, args[1:]) 42 | case adduser.CommandName: 43 | command, err = adduser.NewCommand(lc, configuration, args[1:]) 44 | case deluser.CommandName: 45 | command, err = deluser.NewCommand(lc, configuration, args[1:]) 46 | default: 47 | command = nil 48 | err = fmt.Errorf("unsupported command %s", commandName) 49 | } 50 | 51 | return command, err 52 | } 53 | -------------------------------------------------------------------------------- /internal/security/config/command/proxy/deluser/testdata/token.json: -------------------------------------------------------------------------------- 1 | {"root_token":"abcd"} 2 | -------------------------------------------------------------------------------- /internal/security/config/interfaces/command.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package interfaces 8 | 9 | const ( 10 | // StatusCodeExitNormal exit code 11 | StatusCodeExitNormal = 0 12 | // StatusCodeNoOptionSelected exit code 13 | StatusCodeNoOptionSelected = 1 14 | // StatusCodeExitWithError is exit code for error 15 | StatusCodeExitWithError = 2 16 | ) 17 | 18 | // Command implement the Command pattern 19 | type Command interface { 20 | Execute() (statusCode int, err error) 21 | } 22 | -------------------------------------------------------------------------------- /internal/security/config/interfaces/constants.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package interfaces 8 | 9 | const ( 10 | // JwtTokenType is passed on command line to select JWT auth tokens 11 | JwtTokenType string = "jwt" 12 | // OAuth2TokenType is passed on command line to select OAuth2 auth tokens 13 | OAuth2TokenType string = "oauth2" 14 | // RS256 JWT Alg RS256 15 | RS256 string = "RS256" 16 | // ES256 JWT Alt ES256 17 | ES256 string = "ES256" 18 | ) 19 | -------------------------------------------------------------------------------- /internal/security/fileprovider/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | 17 | package container 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/security/fileprovider/config" 21 | 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | ) 24 | 25 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 26 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 27 | 28 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 29 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 30 | return get(ConfigurationName).(*config.ConfigurationStruct) 31 | } 32 | -------------------------------------------------------------------------------- /internal/security/fileprovider/mocks/mock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Intel Corporation 3 | // Copyright (C) 2025 IOTech Ltd 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | // in compliance with the License. You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software distributed under the License 11 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | // or implied. See the License for the specific language governing permissions and limitations under 13 | // the License. 14 | // 15 | // SPDX-License-Identifier: Apache-2.0 16 | // 17 | 18 | package mocks 19 | 20 | import ( 21 | "github.com/edgexfoundry/edgex-go/internal/security/fileprovider/config" 22 | secretStoreConfig "github.com/edgexfoundry/edgex-go/internal/security/secretstore/config" 23 | 24 | "github.com/stretchr/testify/mock" 25 | ) 26 | 27 | type MockTokenProvider struct { 28 | mock.Mock 29 | } 30 | 31 | // Run see interface.go 32 | func (p *MockTokenProvider) Run() error { 33 | // Boilerplate that returns whatever Mock.On().Returns() is configured for 34 | arguments := p.Called() 35 | return arguments.Error(0) 36 | } 37 | 38 | func (p *MockTokenProvider) SetConfiguration(secretConfig secretStoreConfig.SecretStoreInfo, tokenConfig config.TokenFileProviderInfo) { 39 | // Boilerplate that returns whatever Mock.On().Returns() is configured for 40 | p.Called(secretConfig, tokenConfig) 41 | } 42 | 43 | func (p *MockTokenProvider) RegenToken(entityId string) error { 44 | // Boilerplate that returns whatever Mock.On().Returns() is configured for 45 | p.Called(entityId) 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/security/fileprovider/res/edgex-privileged-token-creator.hcl: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software distributed under the License 10 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | // or implied. See the License for the specific language governing permissions and limitations under 12 | // the License. 13 | // 14 | // SPDX-License-Identifier: Apache-2.0' 15 | // 16 | 17 | // 18 | // This file is taken from 19 | // https://raw.githubusercontent.com/edgexfoundry/edgex-docs/master/security/token-file-provider.1.rst 20 | // 21 | // This is a reference copy of the policy that security-file-token-provider requires 22 | // in order to run and create policies for other services. 23 | // 24 | 25 | path "auth/token/create" { 26 | capabilities = ["create", "update", "sudo"] 27 | } 28 | 29 | path "auth/token/create-orphan" { 30 | capabilities = ["create", "update", "sudo"] 31 | } 32 | 33 | path "auth/token/create/*" { 34 | capabilities = ["create", "update", "sudo"] 35 | } 36 | 37 | path "sys/policies/acl/edgex-service-*" 38 | { 39 | capabilities = ["create", "read", "update", "delete" ] 40 | } 41 | 42 | path "sys/policies/acl" 43 | { 44 | capabilities = ["list"] 45 | } -------------------------------------------------------------------------------- /internal/security/fileprovider/tokenprovider/interface.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software distributed under the License 10 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | // or implied. See the License for the specific language governing permissions and limitations under 12 | // the License. 13 | // 14 | // SPDX-License-Identifier: Apache-2.0 15 | // 16 | 17 | package tokenprovider 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/security/fileprovider/config" 21 | secretstoreConfig "github.com/edgexfoundry/edgex-go/internal/security/secretstore/config" 22 | ) 23 | 24 | // TokenProvider is the interface that the main program expects 25 | // for implemeneting token generation 26 | type TokenProvider interface { 27 | // Set configuration 28 | SetConfiguration(secretStore secretstoreConfig.SecretStoreInfo, tokenConfig config.TokenFileProviderInfo) 29 | // Generate tokens 30 | Run() error 31 | // RegenToken regenerates a token for the specified entity in secret store 32 | RegenToken(entityId string) error 33 | } 34 | -------------------------------------------------------------------------------- /internal/security/fileprovider/types.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software distributed under the License 10 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | // or implied. See the License for the specific language governing permissions and limitations under 12 | // the License. 13 | // 14 | // SPDX-License-Identifier: Apache-2.0 15 | // 16 | 17 | package fileprovider 18 | 19 | type TokenFileProviderInfo struct { 20 | // Path to Vault authorization token to be used by the service 21 | PrivilegedTokenPath string 22 | // Configuration file used to control token creation 23 | ConfigFile string 24 | // Base directory for token file output 25 | OutputDir string 26 | // File name for token file 27 | OutputFilename string 28 | } 29 | -------------------------------------------------------------------------------- /internal/security/fileprovider/util.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Intel Corporation 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | // in compliance with the License. You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software distributed under the License 10 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | // or implied. See the License for the specific language governing permissions and limitations under 12 | // the License. 13 | // 14 | // SPDX-License-Identifier: Apache-2.0 15 | // 16 | 17 | package fileprovider 18 | 19 | func mergeMaps(defaults map[string]interface{}, other map[string]interface{}) map[string]interface{} { 20 | // Defaults are overridden 21 | for key, value := range other { 22 | defaults[key] = value 23 | } 24 | return defaults 25 | } 26 | -------------------------------------------------------------------------------- /internal/security/fileprovider/util_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2019 Intel Corporation 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 4 | // in compliance with the License. You may obtain a copy of the License at 5 | // 6 | // http://www.apache.org/licenses/LICENSE-2.0 7 | // 8 | // Unless required by applicable law or agreed to in writing, software distributed under the License 9 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 10 | // or implied. See the License for the specific language governing permissions and limitations under 11 | // the License. 12 | // 13 | // SPDX-License-Identifier: Apache-2.0 14 | package fileprovider 15 | 16 | import ( 17 | "testing" 18 | 19 | "github.com/stretchr/testify/assert" 20 | ) 21 | 22 | func TestMergeMaps(t *testing.T) { 23 | // Arrange 24 | defaults := map[string]interface{}{"notoverridden": true, "overridden": false} 25 | other := map[string]interface{}{"overridden": true, "newkey": true} 26 | 27 | // Act 28 | merged := mergeMaps(defaults, other) 29 | 30 | // Assert 31 | assert.True(t, merged["notoverridden"].(bool)) 32 | assert.True(t, merged["overridden"].(bool)) 33 | assert.True(t, merged["newkey"].(bool)) 34 | } 35 | -------------------------------------------------------------------------------- /internal/security/kdf/interface.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package kdf 8 | 9 | // KeyDeriver is the interface that the main program expects 10 | // for returning a derived key. 11 | type KeyDeriver interface { 12 | // DeriveKey returns a byte array that is of keyLen length and 13 | // an error if errors where encountered while deriving the key 14 | // inputKeyingMaterial and info are inputs 15 | // to the key deriviation function, an keyLen 16 | // is the desired length of the derived key. 17 | // inputKeyingMaterial is a secret 18 | // and info is used to cause the KDF to generate 19 | // different output keys from the same inputKeyingMaterial. 20 | // Please see the application notes for RFC 5869 21 | // https://tools.ietf.org/html/rfc5869#3 for 22 | // details for details about the key derivation algorithm. 23 | DeriveKey(inputKeyingMaterial []byte, keyLen uint, info string) ([]byte, error) 24 | } 25 | -------------------------------------------------------------------------------- /internal/security/kdf/mocks/mock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package mocks 8 | 9 | import ( 10 | "github.com/stretchr/testify/mock" 11 | ) 12 | 13 | type MockKeyDeriver struct { 14 | mock.Mock 15 | } 16 | 17 | func (m *MockKeyDeriver) DeriveKey(ikm []byte, keyLen uint, info string) ([]byte, error) { 18 | // Boilerplate that returns whatever Mock.On().Returns() is configured for 19 | arguments := m.Called(ikm, keyLen, info) 20 | return arguments.Get(0).([]byte), arguments.Error(1) 21 | } 22 | -------------------------------------------------------------------------------- /internal/security/kdf/mocks/mock_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package mocks 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/edgexfoundry/edgex-go/internal/security/kdf" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestMockInterfaceType(t *testing.T) { 17 | // Typecast will fail if doesn't implement interface properly 18 | var iface KeyDeriver = &MockKeyDeriver{} 19 | assert.NotNil(t, iface) 20 | } 21 | 22 | func TestDeriveKey(t *testing.T) { 23 | mockClient := &MockKeyDeriver{} 24 | mockClient.On("DeriveKey", make([]byte, 32), uint(32), "info").Return(make([]byte, 1), nil) 25 | 26 | ikm, err := mockClient.DeriveKey(make([]byte, 32), 32, "info") 27 | assert.Nil(t, err) 28 | assert.Equal(t, make([]byte, 1), ikm) 29 | mockClient.AssertExpectations(t) 30 | } 31 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/interface.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package pipedhexreader 8 | 9 | // PipedHexReader is an interface to read hex bytes from 10 | // standard output stream of an executable into a byte array 11 | type PipedHexReader interface { 12 | // ReadHexBytesFromExe invokes executable 13 | // and reads hex bytes from stdout and returns an array 14 | ReadHexBytesFromExe(executablePath string) ([]byte, error) 15 | } 16 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/mocks/mock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package mocks 8 | 9 | import ( 10 | "github.com/stretchr/testify/mock" 11 | ) 12 | 13 | type MockPipedHexReader struct { 14 | mock.Mock 15 | } 16 | 17 | func (m *MockPipedHexReader) ReadHexBytesFromExe(executable string) ([]byte, error) { 18 | // Boilerplate that returns whatever Mock.On().Returns() is configured for 19 | arguments := m.Called(executable) 20 | return arguments.Get(0).([]byte), arguments.Error(1) 21 | } 22 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/mocks/mock_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package mocks 8 | 9 | import ( 10 | "testing" 11 | 12 | . "github.com/edgexfoundry/edgex-go/internal/security/pipedhexreader" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestMockInterfaceType(t *testing.T) { 17 | // Typecast will fail if doesn't implement interface properly 18 | var iface PipedHexReader = &MockPipedHexReader{} 19 | assert.NotNil(t, iface) 20 | } 21 | 22 | func TestReadHexBytesFromExe(t *testing.T) { 23 | mockClient := &MockPipedHexReader{} 24 | mockClient.On("ReadHexBytesFromExe", "/bin/somexe").Return(make([]byte, 1), nil) 25 | 26 | ikm, err := mockClient.ReadHexBytesFromExe("/bin/somexe") 27 | assert.Nil(t, err) 28 | assert.Equal(t, make([]byte, 1), ikm) 29 | mockClient.AssertExpectations(t) 30 | } 31 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/pipedhexreader.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package pipedhexreader 8 | 9 | import ( 10 | "bufio" 11 | "encoding/hex" 12 | "os/exec" 13 | "strings" 14 | ) 15 | 16 | // pipedHexReader stores instance data for the pipedhexreader 17 | type pipedHexReader struct{} 18 | 19 | // NewPipedHexReader creates a new PipedHexReader 20 | func NewPipedHexReader() PipedHexReader { 21 | return &pipedHexReader{} 22 | } 23 | 24 | // ReadHexBytesFromExe see interface.go 25 | func (phr *pipedHexReader) ReadHexBytesFromExe(executablePath string) ([]byte, error) { 26 | sanitizedExecutable, err := exec.LookPath(executablePath) 27 | if err != nil { 28 | return nil, err 29 | } 30 | cmd := exec.Command(sanitizedExecutable) 31 | stdout, err := cmd.StdoutPipe() 32 | if err != nil { 33 | return nil, err 34 | } 35 | if err := cmd.Start(); err != nil { 36 | return nil, err 37 | } 38 | reader := bufio.NewReader(stdout) 39 | // We don't WANT a newline, but code defensively 40 | hexbytes, _ := reader.ReadString('\n') 41 | // Readstring returns non-nil error if delim is not present: ignore this 42 | // StdoutPipe usage is to Wait at the end of the reading logic 43 | // because it closes the readers automatically 44 | if err := cmd.Wait(); err != nil { 45 | return nil, err 46 | } 47 | hexbytes = strings.TrimSuffix(hexbytes, "\n") 48 | bytes, err := hex.DecodeString(hexbytes) 49 | if err != nil { 50 | return nil, err 51 | } 52 | return bytes, nil 53 | } 54 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/pipedhexreader_linux_test.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | // 5 | // Copyright (c) 2020 Intel Corporation 6 | // 7 | // SPDX-License-Identifier: Apache-2.0 8 | // 9 | 10 | package pipedhexreader 11 | 12 | import ( 13 | "encoding/hex" 14 | "testing" 15 | 16 | "github.com/stretchr/testify/require" 17 | ) 18 | 19 | func TestPipedHexReaderNewline(t *testing.T) { 20 | // Arrange 21 | phr := NewPipedHexReader() 22 | expected, _ := hex.DecodeString("12345678") 23 | 24 | // Act 25 | key, err := phr.ReadHexBytesFromExe("./testdata/echowithnewline") 26 | 27 | // Assert 28 | require.NoError(t, err) 29 | require.Equal(t, expected, key) 30 | } 31 | 32 | func TestPipedHexReaderNoNewline(t *testing.T) { 33 | // Arrange 34 | phr := NewPipedHexReader() 35 | expected, _ := hex.DecodeString("12345678") 36 | 37 | // Act 38 | key, err := phr.ReadHexBytesFromExe("./testdata/echowithoutnewline") 39 | 40 | // Assert 41 | require.NoError(t, err) 42 | require.Equal(t, expected, key) 43 | } 44 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/testdata/echowithnewline: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 12345678 4 | -------------------------------------------------------------------------------- /internal/security/pipedhexreader/testdata/echowithoutnewline: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo -n 12345678 4 | -------------------------------------------------------------------------------- /internal/security/proxyauth/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2019 Dell Inc. 3 | * Copyright 2023 Intel Corporation 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 6 | * in compliance with the License. You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software distributed under the License 11 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 12 | * or implied. See the License for the specific language governing permissions and limitations under 13 | * the License. 14 | *******************************************************************************/ 15 | 16 | package container 17 | 18 | import ( 19 | "github.com/edgexfoundry/edgex-go/internal/security/proxyauth/config" 20 | 21 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 22 | ) 23 | 24 | // ConfigurationName contains the name of command's config.ConfigurationStruct implementation in the DIC. 25 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 26 | 27 | // ConfigurationFrom helper function queries the DIC and returns command's config.ConfigurationStruct implementation. 28 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 29 | return get(ConfigurationName).(*config.ConfigurationStruct) 30 | } 31 | -------------------------------------------------------------------------------- /internal/security/proxyauth/container/cryptor.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/pkg/utils/crypto/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // CryptoInterfaceName contains the name of the interfaces.Crypto implementation in the DIC. 15 | var CryptoInterfaceName = di.TypeInstanceToName((*interfaces.Crypto)(nil)) 16 | 17 | // CryptoFrom helper function queries the DIC and returns the interfaces.Cryptor implementation. 18 | func CryptoFrom(get di.Get) interfaces.Crypto { 19 | return get(CryptoInterfaceName).(interfaces.Crypto) 20 | } 21 | -------------------------------------------------------------------------------- /internal/security/proxyauth/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/security/proxyauth/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/security/proxyauth/controller/controller.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package controller 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/io" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | type AuthController struct { 15 | dic *di.Container 16 | reader io.DtoReader 17 | } 18 | 19 | func NewAuthController(dic *di.Container) *AuthController { 20 | return &AuthController{ 21 | dic: dic, 22 | reader: io.NewJsonDtoReader(), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /internal/security/proxyauth/controller/controller_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package controller 7 | 8 | import ( 9 | "testing" 10 | 11 | bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 14 | 15 | "github.com/stretchr/testify/require" 16 | ) 17 | 18 | func mockDic() *di.Container { 19 | return di.NewContainer(di.ServiceConstructorMap{ 20 | bootstrapContainer.LoggingClientInterfaceName: func(get di.Get) interface{} { 21 | return logger.NewMockClient() 22 | }, 23 | }) 24 | } 25 | 26 | func TestNewAuthController(t *testing.T) { 27 | dic := mockDic() 28 | controller := NewAuthController(dic) 29 | 30 | require.NotNil(t, controller) 31 | require.NotNil(t, controller.reader) 32 | } 33 | -------------------------------------------------------------------------------- /internal/security/proxyauth/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "security_proxy_auth" 9 | -------------------------------------------------------------------------------- /internal/security/proxyauth/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/security/proxyauth/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- schema for proxy-auth related tables 7 | CREATE SCHEMA IF NOT EXISTS security_proxy_auth; 8 | -------------------------------------------------------------------------------- /internal/security/proxyauth/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- security_proxy_auth.key_store is used to store the key file 7 | CREATE TABLE IF NOT EXISTS security_proxy_auth.key_store ( 8 | id UUID PRIMARY KEY, 9 | name TEXT NOT NULL UNIQUE, 10 | content TEXT NOT NULL, 11 | created timestamp NOT NULL DEFAULT (now() AT TIME ZONE 'utc'), 12 | modified timestamp NOT NULL DEFAULT (now() AT TIME ZONE 'utc') 13 | ); 14 | -------------------------------------------------------------------------------- /internal/security/proxyauth/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/security/proxyauth/infrastructure/interfaces/db.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | import "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 9 | 10 | type DBClient interface { 11 | AddKey(name string, content string) errors.EdgeX 12 | UpdateKey(name string, content string) errors.EdgeX 13 | ReadKeyContent(name string) (string, errors.EdgeX) 14 | KeyExists(name string) (bool, errors.EdgeX) 15 | } 16 | -------------------------------------------------------------------------------- /internal/security/proxyauth/router.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package proxyauth 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go" 10 | spaController "github.com/edgexfoundry/edgex-go/internal/security/proxyauth/controller" 11 | 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/controller" 13 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/handlers" 14 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 15 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 16 | 17 | "github.com/labstack/echo/v4" 18 | ) 19 | 20 | // LoadRestRoutes generates the routing for API requests 21 | // Authentication is always on for this service, 22 | // as it is called by NGINX to authenticate requests 23 | // and must always authenticate even if the rest of EdgeX does not 24 | func LoadRestRoutes(r *echo.Echo, dic *di.Container, serviceName string) { 25 | authenticationHook := handlers.AutoConfigAuthenticationFunc(dic) 26 | 27 | // Common 28 | _ = controller.NewCommonController(dic, r, serviceName, edgex.Version) 29 | 30 | // Run authentication hook for a nil route 31 | r.GET("/auth", emptyHandler, authenticationHook) 32 | 33 | ac := spaController.NewAuthController(dic) 34 | 35 | r.POST(common.ApiKeyRoute, ac.AddKey, authenticationHook) 36 | // This API will be called within the authenticationHook function itself 37 | r.GET(common.ApiVerificationKeyByIssuerRoute, ac.VerificationKeyByIssuer) 38 | } 39 | -------------------------------------------------------------------------------- /internal/security/proxyauth/utils/key.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 9 | 10 | func SigningKeyName(issuer string) string { 11 | return issuer + "/" + common.SigningKeyType 12 | } 13 | 14 | func VerificationKeyName(issuer string) string { 15 | return issuer + "/" + common.VerificationKeyType 16 | } 17 | -------------------------------------------------------------------------------- /internal/security/proxyauth/utils/key_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package utils 7 | 8 | import ( 9 | "testing" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 12 | 13 | "github.com/stretchr/testify/require" 14 | ) 15 | 16 | func TestSigningKeyName(t *testing.T) { 17 | mockIssuer := "mockIssuer" 18 | expected := mockIssuer + "/" + common.SigningKeyType 19 | result := SigningKeyName(mockIssuer) 20 | require.Equal(t, expected, result) 21 | } 22 | 23 | func TestVerificationKeyName(t *testing.T) { 24 | mockIssuer := "mockIssuer" 25 | expected := mockIssuer + "/" + common.VerificationKeyType 26 | result := VerificationKeyName(mockIssuer) 27 | require.Equal(t, expected, result) 28 | } 29 | -------------------------------------------------------------------------------- /internal/security/secretstore/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | 17 | package container 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/security/secretstore/config" 21 | 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | ) 24 | 25 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 26 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 27 | 28 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 29 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 30 | return get(ConfigurationName).(*config.ConfigurationStruct) 31 | } 32 | -------------------------------------------------------------------------------- /internal/security/secretstore/credentialgenerator.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package secretstore 8 | 9 | import ( 10 | "context" 11 | "crypto/rand" 12 | "encoding/base64" 13 | ) 14 | 15 | const randomBytesLength = 33 // 264 bits of entropy 16 | 17 | // CredentialGenerator is the interface for pluggable password generators 18 | type CredentialGenerator interface { 19 | Generate(ctx context.Context) (string, error) 20 | } 21 | 22 | type defaultCredentialGenerator struct{} 23 | 24 | // NewDefaultCredentialGenerator generates random passwords as base64-encoded strings 25 | func NewDefaultCredentialGenerator() CredentialGenerator { 26 | return &defaultCredentialGenerator{} 27 | } 28 | 29 | // Generate implementation returns base64-encoded randomBytesLength random bytes 30 | func (cg *defaultCredentialGenerator) Generate(_ context.Context) (string, error) { 31 | randomBytes := make([]byte, randomBytesLength) 32 | _, err := rand.Read(randomBytes) // all of salt guaranteed to be filled if err==nil 33 | if err != nil { 34 | return "", err 35 | } 36 | newCredential := base64.StdEncoding.EncodeToString(randomBytes) 37 | return newCredential, nil 38 | } 39 | -------------------------------------------------------------------------------- /internal/security/secretstore/credentialgenerator_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package secretstore 8 | 9 | import ( 10 | "context" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestPasswordsAreRandom(t *testing.T) { 17 | ctx, cancel := context.WithCancel(context.Background()) 18 | cg := NewDefaultCredentialGenerator() 19 | cred1, err := cg.Generate(ctx) 20 | assert.NoError(t, err) 21 | cred2, err := cg.Generate(ctx) 22 | assert.NoError(t, err) 23 | assert.NotEqual(t, cred1, cred2) 24 | defer cancel() 25 | } 26 | -------------------------------------------------------------------------------- /internal/security/secretstore/password_linux_test.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | // 5 | // Copyright (c) 2019 Intel Corporation 6 | // 7 | // SPDX-License-Identifier: Apache-2.0 8 | // 9 | 10 | package secretstore 11 | 12 | import ( 13 | "context" 14 | "net/http" 15 | "testing" 16 | 17 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 18 | 19 | "github.com/stretchr/testify/assert" 20 | "github.com/stretchr/testify/require" 21 | ) 22 | 23 | func TestGenerateWithAPG(t *testing.T) { 24 | rootToken := "s.Ga5jyNq6kNfRMVQk2LY1j9iu" // nolint:gosec 25 | mockLogger := logger.MockLogger{} 26 | ctx, cancel := context.WithCancel(context.Background()) 27 | defer cancel() 28 | // Note: apg only available with gnome-desktop, expected to be missing on server Linux distros 29 | gk := NewPasswordGenerator(mockLogger, "apg", []string{"-a", "1", "-n", "1", "-m", "12", "-x", "64"}) 30 | cr := NewCred(&http.Client{}, rootToken, gk, "", logger.MockLogger{}) 31 | 32 | p1, err := cr.GeneratePassword(ctx) 33 | require.NoError(t, err, "failed to create credential") 34 | p2, err := cr.GeneratePassword(ctx) 35 | require.NoError(t, err, "failed to create credential") 36 | assert.NotEqual(t, p1, p2, "each call to GeneratePassword should return a new password") 37 | } 38 | -------------------------------------------------------------------------------- /internal/security/secretstore/server/handlers/initserver.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (C) 2025 IOTech Ltd 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | *******************************************************************************/ 14 | 15 | package handlers 16 | 17 | import ( 18 | "context" 19 | "sync" 20 | 21 | "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/startup" 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | 24 | "github.com/labstack/echo/v4" 25 | ) 26 | 27 | // Bootstrap contains references to dependencies required by the BootstrapHandler. 28 | type BootstrapServer struct { 29 | router *echo.Echo 30 | } 31 | 32 | // NewBootstrap is a factory method that returns an initialized Bootstrap receiver struct. 33 | func NewBootstrapServer(router *echo.Echo) *BootstrapServer { 34 | return &BootstrapServer{ 35 | router: router, 36 | } 37 | } 38 | 39 | // BootstrapHandler fulfills the BootstrapHandler contract and performs initialization needed by the security-secretstore-setup service to declare the rest routes 40 | func (b *BootstrapServer) BootstrapServerHandler(ctx context.Context, wg *sync.WaitGroup, _ startup.Timer, dic *di.Container) bool { 41 | LoadRestRoutes(b.router, dic) 42 | 43 | return true 44 | } 45 | -------------------------------------------------------------------------------- /internal/security/secretstore/server/handlers/router.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package handlers 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/security/secretstore/controller" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 13 | 14 | "github.com/labstack/echo/v4" 15 | ) 16 | 17 | // LoadRestRoutes generates the routing for API requests 18 | // Authentication is always on for this service, 19 | // as it is called by NGINX to authenticate requests 20 | // and must always authenticate even if the rest of EdgeX does not 21 | func LoadRestRoutes(r *echo.Echo, dic *di.Container) { 22 | ac := controller.NewTokenController(dic) 23 | r.PUT(common.ApiRegenTokenRoute, ac.RegenToken) 24 | } 25 | -------------------------------------------------------------------------------- /internal/security/secretstore/testdata/test-resp-init.json: -------------------------------------------------------------------------------- 1 | { 2 | "keys": [ 3 | "test-keys" 4 | ], 5 | "keys_base64": [ 6 | "test-keys-base64" 7 | ], 8 | "root_token": "test-root-token" 9 | } 10 | -------------------------------------------------------------------------------- /internal/security/secretstore/tokencreatable/tokencreatable.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package tokencreatable 17 | 18 | // RevokeFunc is a function to revoke a token 19 | type RevokeFunc func() 20 | 21 | // CreateTokenFunc is a function to create a token with optional revoke function in return 22 | type CreateTokenFunc func(rootToken string) (createResponse map[string]interface{}, revokeFunc RevokeFunc, err error) 23 | -------------------------------------------------------------------------------- /internal/security/secretstore/tokenfilewriter/tokenfilewriter_test.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2021 Intel Corporation 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | * 14 | *******************************************************************************/ 15 | 16 | package tokenfilewriter 17 | 18 | import ( 19 | "os" 20 | "testing" 21 | 22 | "github.com/stretchr/testify/require" 23 | 24 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 25 | "github.com/edgexfoundry/go-mod-secrets/v4/pkg/token/fileioperformer" 26 | "github.com/edgexfoundry/go-mod-secrets/v4/secrets/mocks" 27 | ) 28 | 29 | var lc logger.LoggingClient 30 | var flOpener fileioperformer.FileIoPerformer 31 | 32 | func TestMain(m *testing.M) { 33 | lc = logger.MockLogger{} 34 | flOpener = fileioperformer.NewDefaultFileIoPerformer() 35 | os.Exit(m.Run()) 36 | } 37 | 38 | func TestNewTokenFileWriter(t *testing.T) { 39 | sc := &mocks.SecretStoreClient{} 40 | tokenFileWriter := NewWriter(lc, sc, flOpener) 41 | require.NotEmpty(t, tokenFileWriter) 42 | } 43 | -------------------------------------------------------------------------------- /internal/security/secretstore/tokenmaintenance/initresp_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package tokenmaintenance 7 | 8 | import ( 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | 14 | "github.com/edgexfoundry/edgex-go/internal/security/secretstore/config" 15 | 16 | "github.com/edgexfoundry/go-mod-core-contracts/v4/clients/logger" 17 | "github.com/edgexfoundry/go-mod-secrets/v4/pkg/token/fileioperformer/mocks" 18 | "github.com/edgexfoundry/go-mod-secrets/v4/pkg/types" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | var ( 24 | sampleJSON = ` 25 | { 26 | "keys": [ 27 | "test-keys" 28 | ], 29 | "keys_base64": [ 30 | "test-keys-base64" 31 | ], 32 | "root_token": "test-root-token" 33 | }` 34 | expectedFolder = "/foo" 35 | expectedFile = "bar.baz" 36 | ) 37 | 38 | func TestLoadInitResponse(t *testing.T) { 39 | // Arrange 40 | mockLogger := logger.MockLogger{} 41 | fileOpener := &mocks.FileIoPerformer{} 42 | stringReader := strings.NewReader(sampleJSON) 43 | fileOpener.On("OpenFileReader", filepath.Join(expectedFolder, expectedFile), os.O_RDONLY, os.FileMode(0400)).Return(stringReader, nil) 44 | secretConfig := config.SecretStoreInfo{ 45 | TokenFolderPath: expectedFolder, 46 | TokenFile: expectedFile, 47 | } 48 | initResponse := types.InitResponse{} 49 | 50 | // Act 51 | err := LoadInitResponse(mockLogger, fileOpener, secretConfig, &initResponse) 52 | 53 | // Assert 54 | assert.NoError(t, err) 55 | fileOpener.AssertExpectations(t) 56 | } 57 | -------------------------------------------------------------------------------- /internal/security/secretstore/utils/execrunner-mock.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "context" 11 | "io" 12 | 13 | "github.com/stretchr/testify/mock" 14 | ) 15 | 16 | type MockExecRunner struct { 17 | mock.Mock 18 | } 19 | 20 | func (m *MockExecRunner) SetStdout(stdout io.Writer) { 21 | m.Called(stdout) 22 | } 23 | 24 | func (m *MockExecRunner) LookPath(file string) (string, error) { 25 | arguments := m.Called(file) 26 | return arguments.String(0), arguments.Error(1) 27 | } 28 | 29 | func (m *MockExecRunner) CommandContext(ctx context.Context, 30 | name string, arg ...string) CmdRunner { 31 | arguments := m.Called(ctx, name, arg) 32 | return arguments.Get(0).(CmdRunner) 33 | } 34 | 35 | type MockCmd struct { 36 | mock.Mock 37 | } 38 | 39 | func (m *MockCmd) Start() error { 40 | arguments := m.Called() 41 | return arguments.Error(0) 42 | } 43 | 44 | func (m *MockCmd) Wait() error { 45 | arguments := m.Called() 46 | return arguments.Error(0) 47 | } 48 | -------------------------------------------------------------------------------- /internal/security/secretstore/utils/execrunner.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Intel Corporation 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "context" 11 | "io" 12 | "os" 13 | "os/exec" 14 | ) 15 | 16 | // CmdRunner is mockable interface for golang's exec.Cmd 17 | type CmdRunner interface { 18 | Start() error 19 | Wait() error 20 | } 21 | 22 | // ExecRunner is mockable interface for wrapping os/exec functionality 23 | type ExecRunner interface { 24 | SetStdout(stdout io.Writer) 25 | LookPath(file string) (string, error) 26 | CommandContext(ctx context.Context, name string, arg ...string) CmdRunner 27 | } 28 | 29 | type execWrapper struct { 30 | Stdout io.Writer 31 | Stderr io.Writer 32 | } 33 | 34 | // NewDefaultExecRunner creates an os/exec wrapper 35 | // that joins subprocesses' stdout and stderr with the caller's 36 | func NewDefaultExecRunner() ExecRunner { 37 | return &execWrapper{ 38 | Stdout: os.Stdout, 39 | Stderr: os.Stderr, 40 | } 41 | } 42 | 43 | // SetStdout allows overriding of stdout capture (for consuming password generator output) 44 | func (w *execWrapper) SetStdout(stdout io.Writer) { 45 | w.Stdout = stdout 46 | } 47 | 48 | // LookPath wraps os/exec.LookPath 49 | func (w *execWrapper) LookPath(file string) (string, error) { 50 | return exec.LookPath(file) 51 | } 52 | 53 | // CommandContext wraps os/exec.CommandContext 54 | func (w *execWrapper) CommandContext(ctx context.Context, name string, arg ...string) CmdRunner { 55 | cmd := exec.CommandContext(ctx, name, arg...) 56 | cmd.Stdout = w.Stdout 57 | cmd.Stderr = w.Stderr 58 | return cmd 59 | } 60 | -------------------------------------------------------------------------------- /internal/security/spiffetokenprovider/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (c) 2022 Intel Corporation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | 17 | package container 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/security/spiffetokenprovider/config" 21 | 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | ) 24 | 25 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 26 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 27 | 28 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 29 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 30 | return get(ConfigurationName).(*config.ConfigurationStruct) 31 | } 32 | -------------------------------------------------------------------------------- /internal/support/notifications/application/channel/container.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2021-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package channel 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 10 | ) 11 | 12 | // RESTSenderName contains the name of the channel.RESTSender implementation in the DIC. 13 | var RESTSenderName = di.TypeInstanceToName(RESTSender{}) 14 | 15 | // EmailSenderName contains the name of the channel.EmailSender implementation in the DIC. 16 | var EmailSenderName = di.TypeInstanceToName(EmailSender{}) 17 | 18 | // MQTTSenderName contains the name of the channel.MQTTSender implementation in the DIC. 19 | var MQTTSenderName = di.TypeInstanceToName(MQTTSender{}) 20 | 21 | // ZeroMQTSenderName contains the name of the channel.ZeroMQSender implementation in the DIC. 22 | var ZeroMQTSenderName = di.TypeInstanceToName(ZeroMQSender{}) 23 | 24 | // RESTSenderFrom helper function queries the DIC and returns the channel.Sender implementation. 25 | func RESTSenderFrom(get di.Get) Sender { 26 | return get(RESTSenderName).(Sender) 27 | } 28 | 29 | // EmailSenderFrom helper function queries the DIC and returns the channel.Sender implementation. 30 | func EmailSenderFrom(get di.Get) Sender { 31 | return get(EmailSenderName).(Sender) 32 | } 33 | 34 | // MQTTSenderFrom helper function queries the DIC and returns the channel.Sender implementation. 35 | func MQTTSenderFrom(get di.Get) Sender { 36 | return get(MQTTSenderName).(Sender) 37 | } 38 | 39 | // ZeroMQSenderFrom helper function queries the DIC and returns the channel.Sender implementation. 40 | func ZeroMQSenderFrom(get di.Get) Sender { 41 | return get(ZeroMQTSenderName).(Sender) 42 | } 43 | -------------------------------------------------------------------------------- /internal/support/notifications/application/channel/mocks/Sender.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery v2.15.0. DO NOT EDIT. 2 | 3 | package mocks 4 | 5 | import ( 6 | errors "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 7 | mock "github.com/stretchr/testify/mock" 8 | 9 | models "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 10 | ) 11 | 12 | // Sender is an autogenerated mock type for the Sender type 13 | type Sender struct { 14 | mock.Mock 15 | } 16 | 17 | // Send provides a mock function with given fields: notification, address 18 | func (_m *Sender) Send(notification models.Notification, address models.Address) (string, errors.EdgeX) { 19 | ret := _m.Called(notification, address) 20 | 21 | var r0 string 22 | if rf, ok := ret.Get(0).(func(models.Notification, models.Address) string); ok { 23 | r0 = rf(notification, address) 24 | } else { 25 | r0 = ret.Get(0).(string) 26 | } 27 | 28 | var r1 errors.EdgeX 29 | if rf, ok := ret.Get(1).(func(models.Notification, models.Address) errors.EdgeX); ok { 30 | r1 = rf(notification, address) 31 | } else { 32 | if ret.Get(1) != nil { 33 | r1 = ret.Get(1).(errors.EdgeX) 34 | } 35 | } 36 | 37 | return r0, r1 38 | } 39 | 40 | type mockConstructorTestingTNewSender interface { 41 | mock.TestingT 42 | Cleanup(func()) 43 | } 44 | 45 | // NewSender creates a new instance of Sender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 46 | func NewSender(t mockConstructorTestingTNewSender) *Sender { 47 | mock := &Sender{} 48 | mock.Mock.Test(t) 49 | 50 | t.Cleanup(func() { mock.AssertExpectations(t) }) 51 | 52 | return mock 53 | } 54 | -------------------------------------------------------------------------------- /internal/support/notifications/container/config.go: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright 2017 Dell Inc. 3 | * Copyright 2018 Dell Technologies Inc. 4 | * Copyright (c) 2019 Intel Corporation 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 7 | * in compliance with the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software distributed under the License 12 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 13 | * or implied. See the License for the specific language governing permissions and limitations under 14 | * the License. 15 | *******************************************************************************/ 16 | 17 | package container 18 | 19 | import ( 20 | "github.com/edgexfoundry/edgex-go/internal/support/notifications/config" 21 | 22 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 23 | ) 24 | 25 | // ConfigurationName contains the name of the config.ConfigurationStruct implementation in the DIC. 26 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 27 | 28 | // ConfigurationFrom helper function queries the DIC and returns the config.ConfigurationStruct implementation. 29 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 30 | return get(ConfigurationName).(*config.ConfigurationStruct) 31 | } 32 | -------------------------------------------------------------------------------- /internal/support/notifications/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2020-2021 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/support/notifications/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/support/notifications/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "support_notifications" 9 | -------------------------------------------------------------------------------- /internal/support/notifications/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/support/notifications/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- schema for support_notifications related tables 7 | CREATE SCHEMA IF NOT EXISTS support_notifications; 8 | -------------------------------------------------------------------------------- /internal/support/notifications/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- support_notifications.notification is used to store the notification information 7 | CREATE TABLE IF NOT EXISTS support_notifications.notification ( 8 | id UUID PRIMARY KEY, 9 | content JSONB NOT NULL -- Note that this content is not the same as the content in Notification model 10 | ); 11 | 12 | -- support_notifications.subscription is used to store the subscription information 13 | CREATE TABLE IF NOT EXISTS support_notifications.subscription ( 14 | id UUID PRIMARY KEY, 15 | content JSONB NOT NULL 16 | ); 17 | 18 | -- support_notifications.transmission is used to store the transmission information 19 | CREATE TABLE IF NOT EXISTS support_notifications.transmission ( 20 | id UUID PRIMARY KEY, 21 | notification_id UUID NOT NULL, 22 | content JSONB NOT NULL, 23 | CONSTRAINT fk_notification 24 | FOREIGN KEY(notification_id) 25 | REFERENCES support_notifications.notification(id) 26 | ON DELETE CASCADE 27 | ); 28 | -------------------------------------------------------------------------------- /internal/support/notifications/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/support/scheduler/application/action/devicecontrol.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package action 7 | 8 | import ( 9 | "context" 10 | "encoding/json" 11 | 12 | bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 13 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 14 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 15 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 16 | ) 17 | 18 | func issueSetCommand(dic *di.Container, action models.DeviceControlAction) (string, errors.EdgeX) { 19 | if action.DeviceName == "" { 20 | return "", errors.NewCommonEdgeX(errors.KindContractInvalid, "device name cannot be empty", nil) 21 | } 22 | 23 | if action.SourceName == "" { 24 | return "", errors.NewCommonEdgeX(errors.KindContractInvalid, "source name cannot be empty", nil) 25 | } 26 | 27 | var payload map[string]any 28 | if err := json.Unmarshal(action.Payload, &payload); err != nil { 29 | return "", errors.NewCommonEdgeX(errors.KindContractInvalid, "failed to convert payload to map", err) 30 | } 31 | 32 | cc := bootstrapContainer.CommandClientFrom(dic.Get) 33 | if cc == nil { 34 | return "", errors.NewCommonEdgeX(errors.KindServerError, "nil CommandClient returned", nil) 35 | } 36 | 37 | resp, err := cc.IssueSetCommandByName(context.Background(), action.DeviceName, action.SourceName, payload) 38 | if err != nil { 39 | return "", err 40 | } 41 | 42 | return resp.Message, nil 43 | } 44 | -------------------------------------------------------------------------------- /internal/support/scheduler/application/action/edgexmessagebus.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024-2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package action 7 | 8 | import ( 9 | "context" 10 | 11 | bootstrapContainer "github.com/edgexfoundry/go-mod-bootstrap/v4/bootstrap/container" 12 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 13 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 14 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 15 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 16 | "github.com/edgexfoundry/go-mod-messaging/v4/pkg/types" 17 | ) 18 | 19 | func publishEdgeXMessageBus(dic *di.Container, action models.EdgeXMessageBusAction) errors.EdgeX { 20 | messageBus := bootstrapContainer.MessagingClientFrom(dic.Get) 21 | contentType := action.ContentType 22 | if contentType == "" { 23 | contentType = common.ContentTypeJSON 24 | } 25 | ctx := context.WithValue(context.Background(), common.ContentType, contentType) //nolint: staticcheck 26 | 27 | envelope := types.NewMessageEnvelope(action.Payload, ctx) 28 | 29 | if err := messageBus.Publish(envelope, action.Topic); err != nil { 30 | return errors.NewCommonEdgeX(errors.KindServerError, "failed to publish to EdgeX message bus", err) 31 | } 32 | 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /internal/support/scheduler/container/config.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 10 | 11 | "github.com/edgexfoundry/edgex-go/internal/support/scheduler/config" 12 | ) 13 | 14 | // ConfigurationName contains the name of scheduler's config.ConfigurationStruct implementation in the DIC. 15 | var ConfigurationName = di.TypeInstanceToName(config.ConfigurationStruct{}) 16 | 17 | // ConfigurationFrom helper function queries the DIC and returns scheduler's config.ConfigurationStruct implementation. 18 | func ConfigurationFrom(get di.Get) *config.ConfigurationStruct { 19 | return get(ConfigurationName).(*config.ConfigurationStruct) 20 | } 21 | -------------------------------------------------------------------------------- /internal/support/scheduler/container/database.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/edgex-go/internal/support/scheduler/infrastructure/interfaces" 10 | 11 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 12 | ) 13 | 14 | // DBClientInterfaceName contains the name of the interfaces.DBClient implementation in the DIC. 15 | var DBClientInterfaceName = di.TypeInstanceToName((*interfaces.DBClient)(nil)) 16 | 17 | // DBClientFrom helper function queries the DIC and returns the interfaces.DBClient implementation. 18 | func DBClientFrom(get di.Get) interfaces.DBClient { 19 | return get(DBClientInterfaceName).(interfaces.DBClient) 20 | } 21 | -------------------------------------------------------------------------------- /internal/support/scheduler/container/scheduler.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package container 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-bootstrap/v4/di" 10 | 11 | "github.com/edgexfoundry/edgex-go/internal/support/scheduler/infrastructure/interfaces" 12 | ) 13 | 14 | // SchedulerManagerName contains the name of the interfaces.SchedulerManager implementation in the DIC. 15 | var SchedulerManagerName = di.TypeInstanceToName((*interfaces.SchedulerManager)(nil)) 16 | 17 | // SchedulerManagerFrom helper function queries the DIC and returns the interfaces.SchedulerManager implementation. 18 | func SchedulerManagerFrom(get di.Get) interfaces.SchedulerManager { 19 | return get(SchedulerManagerName).(interfaces.SchedulerManager) 20 | } 21 | -------------------------------------------------------------------------------- /internal/support/scheduler/controller/http/utils_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package http 7 | 8 | import ( 9 | "net/http" 10 | 11 | "github.com/edgexfoundry/go-mod-core-contracts/v4/common" 12 | "github.com/edgexfoundry/go-mod-core-contracts/v4/dtos" 13 | ) 14 | 15 | const ( 16 | exampleUUID = "82eb2e26-0f24-48aa-ae4c-de9dac3fb9bc" 17 | testCorrelationID = "" 18 | testScheduleJobName = "jobName" 19 | testStatus = "SUCCEEDED" 20 | testTimestamp = 1723642440000 21 | ) 22 | 23 | var ( 24 | testScheduleJobLabels = []string{"label"} 25 | testScheduleDef = dtos.ScheduleDef{ 26 | Type: common.DefInterval, 27 | IntervalScheduleDef: dtos.IntervalScheduleDef{ 28 | Interval: "10m", 29 | }, 30 | } 31 | testScheduleAction = dtos.ScheduleAction{ 32 | Type: common.ActionEdgeXMessageBus, 33 | ContentType: common.ContentTypeJSON, 34 | Payload: nil, 35 | EdgeXMessageBusAction: dtos.EdgeXMessageBusAction{ 36 | Topic: "testTopic", 37 | }, 38 | } 39 | testScheduleActions = []dtos.ScheduleAction{ 40 | testScheduleAction, 41 | { 42 | Type: common.ActionREST, 43 | ContentType: common.ContentTypeJSON, 44 | Payload: nil, 45 | RESTAction: dtos.RESTAction{ 46 | Address: "testAddress", 47 | Method: http.MethodGet, 48 | }, 49 | }, 50 | } 51 | ) 52 | -------------------------------------------------------------------------------- /internal/support/scheduler/embed/consts.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | const SchemaName = "support_scheduler" 9 | -------------------------------------------------------------------------------- /internal/support/scheduler/embed/schema.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2025 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package embed 7 | 8 | import "embed" 9 | 10 | // SQLFiles contains the SQL files as embedded resources. 11 | // Following code use go embed directive to embed the SQL files into the binary. 12 | 13 | //go:embed sql 14 | var SQLFiles embed.FS 15 | 16 | // The SQL files are stored in the sql directory with two subdirectories: idempotent and versions. 17 | // 1. idempotent: directory contains the SQL files that can be initialized the db schema. 18 | // The SQL files in this directory are designed to be idempotent and can be executed multiple times without changing 19 | // the result. 20 | // 2. versions: directory contains various version subdirectories with the SQL files that are used to update table 21 | // schema per versions. 22 | // 23 | // When any future requirements need to alter the table schema, the practice is to AVOID directly update SQL files in 24 | // idempotent directory. Instead, create a new subdirectory with the new semantic version number. Add new SQL files to 25 | // update the schema into the new version subdirectory. The SQL files in the new version subdirectory should be named 26 | // with the format of -.sql. Moreover, when naming the new version subdirectory, follow 27 | // the semantic versioning rules as defined in https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions. 28 | // The valid semver format is ::= "-" , so use -dev rather than .dev as 29 | // pre-release suffix for semver to parse correctly. 30 | -------------------------------------------------------------------------------- /internal/support/scheduler/embed/sql/idempotent/00-utils.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- schema for support_scheduler related tables 7 | CREATE SCHEMA IF NOT EXISTS support_scheduler; 8 | -------------------------------------------------------------------------------- /internal/support/scheduler/embed/sql/idempotent/01-tables.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2024 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- support_scheduler.job is used to store the schedule job information 7 | CREATE TABLE IF NOT EXISTS support_scheduler.job ( 8 | id UUID PRIMARY KEY, 9 | content JSONB NOT NULL 10 | ); 11 | 12 | -- support_scheduler.record is used to store the schedule action record 13 | CREATE TABLE IF NOT EXISTS support_scheduler.record ( 14 | id UUID PRIMARY KEY, 15 | action_id UUID NOT NULL, 16 | job_name TEXT NOT NULL, 17 | action JSONB NOT NULL, 18 | status TEXT NOT NULL, 19 | scheduled_at timestamp NOT NULL, 20 | created timestamp NOT NULL DEFAULT (now() AT TIME ZONE 'utc') 21 | ); 22 | -------------------------------------------------------------------------------- /internal/support/scheduler/embed/sql/versions/4.0.0-dev/00-placeholder.sql: -------------------------------------------------------------------------------- 1 | -- 2 | -- Copyright (C) 2025 IOTech Ltd 3 | -- 4 | -- SPDX-License-Identifier: Apache-2.0 5 | 6 | -- this is a placeholder file for the 4.0.0-dev version of the database schema 7 | -------------------------------------------------------------------------------- /internal/support/scheduler/infrastructure/interfaces/SchedulerManager.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2024 IOTech Ltd 3 | // 4 | // SPDX-License-Identifier: Apache-2.0 5 | 6 | package interfaces 7 | 8 | import ( 9 | "github.com/edgexfoundry/go-mod-core-contracts/v4/errors" 10 | "github.com/edgexfoundry/go-mod-core-contracts/v4/models" 11 | ) 12 | 13 | type SchedulerManager interface { 14 | AddScheduleJob(job models.ScheduleJob, correlationId string) errors.EdgeX 15 | UpdateScheduleJob(job models.ScheduleJob, correlationId string) errors.EdgeX 16 | DeleteScheduleJobByName(name, correlationId string) errors.EdgeX 17 | StartScheduleJobByName(name, correlationId string) errors.EdgeX 18 | StopScheduleJobByName(name, correlationId string) errors.EdgeX 19 | TriggerScheduleJobByName(name, correlationId string) errors.EdgeX 20 | ValidateUpdatingScheduleJob(job models.ScheduleJob) errors.EdgeX 21 | 22 | Shutdown(correlationId string) errors.EdgeX 23 | } 24 | -------------------------------------------------------------------------------- /openapi/README.md: -------------------------------------------------------------------------------- 1 | # EdgeX Foundry V3 API Specification 2 | OpenAPI docs defining V3 API for the EdgeX Foundry platform 3 | 4 | To view the Swagger definition: 5 | 6 | 1.) Point your browser to https://editor.swagger.io/ 7 | 8 | 2.) Choose File --> Import from URL 9 | 10 | 3.) Choose a YAML file to view, then click the "RAW" button and use that URL 11 | 12 | 4.) You should see the Swagger UI output on the right. 13 | -------------------------------------------------------------------------------- /security.txt: -------------------------------------------------------------------------------- 1 | # This file is intended for inclusion in official EdgeX binary releases, 2 | # such as containers. 3 | 4 | # To privately report a security vulnerability in EdgeX Foundry 5 | # in the upstream open source repository hosted on github.com. 6 | 7 | Contact: mailto:security-issues@lists.edgexfoundry.org 8 | 9 | Preferred-Languages: en 10 | 11 | Canonical: https://github.com/edgexfoundry/edgex-go/blob/main/security.txt 12 | 13 | Policy: https://wiki.edgexfoundry.org/display/FA/Security 14 | 15 | Expires: 2024-06-30T00:00:00.000Z 16 | 17 | # This file conforms to RFC 9116: https://www.rfc-editor.org/rfc/rfc9116 18 | # See also https://securitytxt.org/ 19 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2021 3 | // Cavium 4 | // Mainflux 5 | // IOTech 6 | // 7 | // SPDX-License-Identifier: Apache-2.0 8 | // 9 | 10 | package edgex 11 | 12 | // Global version for edgex-go 13 | var Version string = "to be replaced by makefile" 14 | --------------------------------------------------------------------------------