From ed92dcf39be40f7f52ac48771b086ff991d71b0c Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Wed, 5 May 2021 10:59:32 +0200 Subject: [PATCH 1/3] auth for sandbox --- .asciidoctorconfig | 1 + .../src/docs/asciidoc/.asciidoctorconfig | 1 + docs/guide/src/docs/asciidoc/usage.adoc | 15 ++++- .../micronaut-console-example-function.gradle | 51 ++++++++++++++- .../console/function/AuthConsoleHandler.java | 54 ++++++++++++++++ .../console/function/AuthorizedScript.java | 62 +++++++++++++++++++ .../function/AuthConsoleHandlerSpec.groovy | 40 ++++++++++++ .../AuthConsoleHandlerSpec/error.groovy | 1 + .../AuthConsoleHandlerSpec/prints.groovy | 4 ++ .../console/AuthConsoleHandlerSpec/prints.txt | 5 ++ 10 files changed, 232 insertions(+), 2 deletions(-) create mode 100644 .asciidoctorconfig create mode 100644 docs/guide/src/docs/asciidoc/.asciidoctorconfig create mode 100644 libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandler.java create mode 100644 libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java create mode 100644 libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy create mode 100644 libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/error.groovy create mode 100644 libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.groovy create mode 100644 libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.txt diff --git a/.asciidoctorconfig b/.asciidoctorconfig new file mode 100644 index 0000000..c257e20 --- /dev/null +++ b/.asciidoctorconfig @@ -0,0 +1 @@ +:root-dir: {asciidoctorconfigdir} diff --git a/docs/guide/src/docs/asciidoc/.asciidoctorconfig b/docs/guide/src/docs/asciidoc/.asciidoctorconfig new file mode 100644 index 0000000..4ccdf0c --- /dev/null +++ b/docs/guide/src/docs/asciidoc/.asciidoctorconfig @@ -0,0 +1 @@ +:includedir: {asciidoctorconfigdir} diff --git a/docs/guide/src/docs/asciidoc/usage.adoc b/docs/guide/src/docs/asciidoc/usage.adoc index 87631e8..8002bb5 100644 --- a/docs/guide/src/docs/asciidoc/usage.adoc +++ b/docs/guide/src/docs/asciidoc/usage.adoc @@ -29,13 +29,26 @@ You can use Micronaut Console to create sandbox function to execute arbitrary co Add the following lines to your either existing or a brand new Micronaut function: -[source, groovy] +[source, groovy, role="primary"] +.Basic ---- +include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=import-runtime] include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=import] include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=tasks] ---- +[source, groovy, role="secondary"] +.Authorized +---- +include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=import-runtime] +include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=import-sts] +include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=import] + +include::{root-dir}/examples/micronaut-console-example-function/micronaut-console-example-function.gradle[tags=auth] +---- + + Then you can invoke the function from the command line as follows (assuming you have set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_KEY` environment variables) [source,shell script] diff --git a/examples/micronaut-console-example-function/micronaut-console-example-function.gradle b/examples/micronaut-console-example-function/micronaut-console-example-function.gradle index bd91ba0..c5dcc14 100644 --- a/examples/micronaut-console-example-function/micronaut-console-example-function.gradle +++ b/examples/micronaut-console-example-function/micronaut-console-example-function.gradle @@ -15,9 +15,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// tag::import[] +// tag::import-runtime[] import com.amazonaws.services.lambda.model.InvocationType import com.amazonaws.services.lambda.model.Runtime +// end::import-runtime[] +// tag::import-sts[] +import com.amazonaws.services.securitytoken.AWSSecurityTokenService +import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient +import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest +import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult +// end::import-sts[] +// tag::import[] import groovy.json.JsonOutput import groovy.json.JsonSlurper import jp.classmethod.aws.gradle.lambda.AWSLambdaInvokeTask @@ -123,6 +131,7 @@ jar { } +/* // tag::tasks[] task deploySandbox(type: AWSLambdaMigrateFunctionTask, dependsOn: shadowJar) { functionName = "MicronautConsoleSandbox" @@ -148,3 +157,43 @@ task invokeSandbox(type: AWSLambdaInvokeTask) { } } // end::tasks[] +*/ + +// tag::auth[] +task deploySandbox(type: AWSLambdaMigrateFunctionTask, dependsOn: shadowJar) { + functionName = "MicronautConsoleSandbox" + handler = "com.agorapulse.micronaut.console.function.AuthConsoleHandler::apply" + role = "arn:aws:iam::${aws.accountId}:role/lambda_basic_execution" + runtime = Runtime.Java8 + zipFile = shadowJar.archivePath + memorySize = 512 + timeout = 60 +} + +task invokeSandbox(type: AWSLambdaInvokeTask) { + File scriptFile = file(project.hasProperty('script.file') ? project.getProperty('script.file') : 'src/test/resources/scripts/hello.sandbox.groovy') + + AWSSecurityTokenService sts = aws.createClient(AWSSecurityTokenServiceClient.class, aws.profileName); + sts.setRegion(aws.getActiveRegion(aws.region)); + GetCallerIdentityResult identity = sts.getCallerIdentity(new GetCallerIdentityRequest()) + + + functionName = 'MicronautConsoleSandbox' + invocationType = InvocationType.RequestResponse + payload = JsonOutput.toJson( + body: scriptFile.text, + user: [ + id: identity.arn, + name: identity.userId, + address: '/' + new URL('http://checkip.amazonaws.com/').text + ] + ) + + doLast { + println "\nLambda function result:\n" + println new JsonSlurper().parseText(new String(invokeResult.payload.array(), "UTF-8")) + println() + } +} +// end::auth[] + diff --git a/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandler.java b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandler.java new file mode 100644 index 0000000..6665cbd --- /dev/null +++ b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandler.java @@ -0,0 +1,54 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2021 Agorapulse. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.agorapulse.micronaut.console.function; + +import com.agorapulse.micronaut.console.ConsoleConfiguration; +import com.agorapulse.micronaut.console.ConsoleService; +import com.agorapulse.micronaut.console.Script; +import com.agorapulse.micronaut.console.util.ExceptionSanitizer; +import io.micronaut.context.ApplicationContext; +import io.micronaut.function.FunctionBean; +import io.micronaut.function.executor.FunctionInitializer; + +import javax.inject.Inject; +import java.util.function.Function; + +@FunctionBean(value = "auth-console", method = "apply") +public class AuthConsoleHandler extends FunctionInitializer implements Function { + + @Inject private ConsoleService service; + @Inject private ConsoleConfiguration configuration; + @Inject private ExceptionSanitizer sanitizer; + + @Override + public String apply(AuthorizedScript authorizedScript) { + Script script = new Script(configuration.getLanguage(), authorizedScript.getBody(), authorizedScript.getUser().toUser()); + try { + return service.execute(script).toString(); + } catch (Throwable th) { + return sanitizer.extractMessage(th) + "\n\nScript:\n" + script; + } + } + + public AuthConsoleHandler() { } + + public AuthConsoleHandler(ApplicationContext applicationContext) { + super(applicationContext); + } + +} diff --git a/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java new file mode 100644 index 0000000..f153946 --- /dev/null +++ b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java @@ -0,0 +1,62 @@ +package com.agorapulse.micronaut.console.function; + +import com.agorapulse.micronaut.console.User; +import io.micronaut.core.annotation.Introspected; + +@Introspected +public class AuthorizedScript { + + static class Executor { + private String id; + private String name; + private String address; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public User toUser() { + return new User(id, name, address); + } + } + + private String body; + private Executor user = new Executor(); + + public String getBody() { + return body; + } + + public void setBody(String body) { + this.body = body; + } + + public Executor getUser() { + return user; + } + + public void setUser(Executor user) { + this.user = user; + } + +} diff --git a/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy b/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy new file mode 100644 index 0000000..8f524d9 --- /dev/null +++ b/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy @@ -0,0 +1,40 @@ +package com.agorapulse.micronaut.console.function + +import com.agorapulse.testing.fixt.Fixt +import groovy.transform.CompileDynamic +import spock.lang.AutoCleanup +import spock.lang.Shared +import spock.lang.Specification + +@CompileDynamic +class AuthConsoleHandlerSpec extends Specification { + + @Shared @AutoCleanup AuthConsoleHandler handler = new AuthConsoleHandler() + + Fixt fixt = Fixt.create(ConsoleHandlerSpec) + + void 'execute simple groovy script'() { + expect: + handler.apply(script('"Hello World"')) == 'Hello World' + } + + void 'execute script with prints'() { + expect: + handler.apply(script(fixt.readText('prints.groovy'))) == fixt.readText('prints.txt') + } + + void 'execute script with exception'() { + given: + String errorResult = handler.apply(script(fixt.readText('error.groovy'))) + expect: + errorResult.startsWith('com.agorapulse.micronaut.console.ConsoleException: Exception during script execution') + } + + private AuthorizedScript script(String body) { + return new AuthorizedScript( + body: body, + user: new AuthorizedScript.Executor(id: 'executor', name: 'Script Executor', address: '/10.0.0.1') + ) + } + +} diff --git a/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/error.groovy b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/error.groovy new file mode 100644 index 0000000..7101df7 --- /dev/null +++ b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/error.groovy @@ -0,0 +1 @@ +throw new RuntimeException("Does not work!") diff --git a/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.groovy b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.groovy new file mode 100644 index 0000000..4f2a6b1 --- /dev/null +++ b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.groovy @@ -0,0 +1,4 @@ +println 'Hello Print' + +"Hello Result" + diff --git a/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.txt b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.txt new file mode 100644 index 0000000..331d21c --- /dev/null +++ b/libs/micronaut-console/src/test/resources/com/agorapulse/micronaut/console/AuthConsoleHandlerSpec/prints.txt @@ -0,0 +1,5 @@ +# Out # +Hello Print + +# Result # +Hello Result From 38f19274c0ff5ee93a6922684e20784113a2e3ab Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Wed, 5 May 2021 11:27:44 +0200 Subject: [PATCH 2/3] properly handle when the AWS is not configured --- .../micronaut-console-example-function.gradle | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/micronaut-console-example-function/micronaut-console-example-function.gradle b/examples/micronaut-console-example-function/micronaut-console-example-function.gradle index c5dcc14..61d0259 100644 --- a/examples/micronaut-console-example-function/micronaut-console-example-function.gradle +++ b/examples/micronaut-console-example-function/micronaut-console-example-function.gradle @@ -173,20 +173,24 @@ task deploySandbox(type: AWSLambdaMigrateFunctionTask, dependsOn: shadowJar) { task invokeSandbox(type: AWSLambdaInvokeTask) { File scriptFile = file(project.hasProperty('script.file') ? project.getProperty('script.file') : 'src/test/resources/scripts/hello.sandbox.groovy') - AWSSecurityTokenService sts = aws.createClient(AWSSecurityTokenServiceClient.class, aws.profileName); - sts.setRegion(aws.getActiveRegion(aws.region)); - GetCallerIdentityResult identity = sts.getCallerIdentity(new GetCallerIdentityRequest()) + Map user = [:] + try { + AWSSecurityTokenService sts = aws.createClient(AWSSecurityTokenServiceClient.class, aws.profileName); + sts.region = aws.getActiveRegion(aws.region) + GetCallerIdentityResult identity = sts.getCallerIdentity(new GetCallerIdentityRequest()) + user.id = identity.arn + user.name = identity.userId + user.address = '/' + new URL('http://checkip.amazonaws.com/').text + } catch (Exception ignored) { + logger.info("AWS not configured") + } functionName = 'MicronautConsoleSandbox' invocationType = InvocationType.RequestResponse payload = JsonOutput.toJson( body: scriptFile.text, - user: [ - id: identity.arn, - name: identity.userId, - address: '/' + new URL('http://checkip.amazonaws.com/').text - ] + user: user ) doLast { From 495d3ebba58661ef1f0840c12f628a6e9416a759 Mon Sep 17 00:00:00 2001 From: Vladimir Orany Date: Wed, 5 May 2021 11:41:36 +0200 Subject: [PATCH 3/3] fixed license headers --- build.gradle | 2 ++ .../console/function/AuthorizedScript.java | 17 +++++++++++++++++ .../function/AuthConsoleHandlerSpec.groovy | 17 +++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/build.gradle b/build.gradle index 315eb2d..589034f 100644 --- a/build.gradle +++ b/build.gradle @@ -130,6 +130,8 @@ allprojects { exclude '***.js' exclude '**/ConsoleHandlerSpec/*.groovy' exclude '**/ConsoleHandlerSpec/*.txt' + exclude '**/AuthConsoleHandlerSpec/*.groovy' + exclude '**/AuthConsoleHandlerSpec/*.txt' exclude '**/ConsoleControllerSpec/*.groovy' exclude '**/ConsoleControllerSpec/*.txt' exclude 'external.groovy' diff --git a/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java index f153946..bc3723f 100644 --- a/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java +++ b/libs/micronaut-console/src/main/groovy/com/agorapulse/micronaut/console/function/AuthorizedScript.java @@ -1,3 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2021 Agorapulse. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.agorapulse.micronaut.console.function; import com.agorapulse.micronaut.console.User; diff --git a/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy b/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy index 8f524d9..61a3473 100644 --- a/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy +++ b/libs/micronaut-console/src/test/groovy/com/agorapulse/micronaut/console/function/AuthConsoleHandlerSpec.groovy @@ -1,3 +1,20 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright 2020-2021 Agorapulse. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.agorapulse.micronaut.console.function import com.agorapulse.testing.fixt.Fixt