Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Macro for setting *session*, and code to improve getting credentials from shared config files #36

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 31 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,41 @@

This software is still ALPHA quality. The APIs will be likely to change.

## Usage
## Quick Start

AWS-SDK provides interfaces for each AWS services as individual systems under "aws-sdk/services/*".

Here's an example to send an SMS via Amazon Simple Notification Service:

```common-lisp
(ql:quickload '(:aws-sdk :aws-sdk/services/sns))
;; You must load aws-sdk first, then you can load services
(ql:quickload :aws-sdk)

;; Services can be loaded by name with quicklisp
(ql:quickload :aws-sdk/services/sns)

;; ...or via aws-load
(aws-load sns)

;; "Log in" to AWS by setting *session* via make-session
(setf aws:*session* (aws:make-session))

;; Sending
(aws/sns:publish :phone-number "+8190xxxxxxxx" :message "Hi, there")
```

You can also configure AWS-SDK to use a configured profile for authentication:
```
(setf aws:*session* (aws:make-session :profile "profile_name_here"))
```

You can _also_ also use the `LOG-IN` macro as shorthand:

``` common-lisp
(log-in :profile "profile_name_here")
```


## Configuring the SDK

### Configuring Credentials
Expand All @@ -29,8 +49,7 @@ Before using the SDK, you'll need to set AWS credentials for AWS services. AWS-S
* Shared Credentials file (~/.aws/credentials).
* EC2 Instance Role Credentials

It's also can be configured via `aws:*session*`:

To "log in" to AWS, you must set the `*session*` variable with a session containing your credentials, and any configuration. This can be done via the `make-session` function.
```common-lisp
(setf aws:*session*
(aws:make-session :credentials
Expand All @@ -56,11 +75,19 @@ It's also can be configured via `aws:*session*`:
## Development note

### Generating all services
From a Common Lisp REPL, with the current working directory set to the root of aws-sdk-lisp:
```
(ql:quickload :lake)
(lake:lake)
```

Or, from a command-line with
```
$ lake
```

See: [github.com/takagi/lake](https://github.com/takagi/lake)

## Author

* Eitaro Fukamachi ([email protected])
Expand Down
7 changes: 6 additions & 1 deletion make-session.lisp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(defpackage #:aws-sdk/make-session
(:use #:cl)
(:import-from #:aws-sdk/session
#:*session*
#:%make-session)
(:import-from #:aws-sdk/credentials
#:credentials
Expand All @@ -24,7 +25,8 @@
#:getenv)
(:import-from #:assoc-utils
#:aget)
(:export #:make-session))
(:export #:make-session
#:log-in))
(in-package #:aws-sdk/make-session)

(defun make-session (&key credentials region (profile *aws-profile*))
Expand All @@ -51,3 +53,6 @@
:region (or region
(shared-config-region shared-config)
(getenv "AWS_REGION")))))

(defmacro log-in (&rest args)
`(setf *session* (funcall #'make-session ,@args)))
112 changes: 66 additions & 46 deletions shared-config.lisp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
(:import-from #:aws-sdk/credentials
#:make-credentials)
(:import-from #:aws-sdk/utils/config
#:*aws-profile*
#:read-from-file)
(:import-from #:assoc-utils
#:aget)
Expand All @@ -11,6 +12,7 @@
#:shared-config-credentials
#:shared-config-region
#:shared-config-assume-role
#:shared-config-profile

#:assume-role-config
#:assume-role-role-arn
Expand All @@ -36,59 +38,77 @@
(user-homedir-pathname)))
(config-path (merge-pathnames ".aws/config"
(user-homedir-pathname)))
profile
(profile *aws-profile*)

region
credentials
(assume-role nil :type (or assume-role-config null)))

(defun make-shared-config (&rest args &key credentials-path config-path profile)
(declare (ignore credentials-path config-path))
(let ((shared-config (apply #'%make-shared-config args)))
(let ((section (with-slots (credentials-path profile) shared-config
(when (and credentials-path
(probe-file credentials-path))
(read-from-file credentials-path
:profile profile
:allow-no-profile t)))))
(let ((access-key-id (aget section "aws_access_key_id"))
(secret-access-key (aget section "aws_secret_access_key")))
(when (and access-key-id secret-access-key)
(setf (shared-config-credentials shared-config)
(make-credentials
:access-key-id access-key-id
:secret-access-key secret-access-key
:session-token (aget section "aws_session_token")
:provider-name (format nil "shared-config: ~A"
(shared-config-config-path shared-config))))))
(declare (ignorable credentials-path config-path profile))
(let* ((shared-config (apply #'%make-shared-config args))
(base-config-section (with-slots (config-path profile) shared-config
(when (and config-path
(probe-file config-path))
(read-from-file config-path
:profile profile
:allow-no-profile t))))
(base-creds-section (with-slots (credentials-path profile) shared-config
(when (and credentials-path
(probe-file credentials-path))
(read-from-file credentials-path
:profile profile
:allow-no-profile t))))
(source-profile-name (aget base-config-section "source_profile"))
(source-config-section (when source-profile-name
(with-slots (config-path) shared-config
(when (and config-path
(probe-file config-path))
(read-from-file config-path
:profile source-profile-name)))))
(source-creds-section (when source-profile-name
(with-slots (credentials-path) shared-config
(when (and credentials-path
(probe-file credentials-path))
(read-from-file credentials-path
:profile source-profile-name)))))
(role-arn (aget base-config-section "role_arn"))

(let ((role-arn (aget section "role_arn"))
(source-profile (aget section "source_profile"))
(credential-source (aget section "credential_source")))
(when (and role-arn
(or source-profile credential-source))
(when (and source-profile
credential-source)
(error "Profile ~S has both of source_profile and credential_source"
profile))
(when credential-source
(error "credential_source is not supported yet"))
(setf (shared-config-assume-role shared-config)
(make-assume-role-config :role-arn role-arn
:source-profile source-profile
:credential-source credential-source
:external-id (aget section "external_id")
:serial-number (aget section "mfa_serial")
:role-session-name (aget section "role_session_name"))))))
;; You cannot specify both source_profile and credential_source in the same profile.
(credential-source (if (and (aget base-config-section "credential_source") source-profile-name)
(error "You cannot specify both source_profile and credential_source in the same profile")
(aget base-config-section "credential_source")))
(access-key-id (or (aget base-creds-section "aws_access_key_id")
(aget source-creds-section "aws_access_key_id")))
(secret-access-key (or (aget base-creds-section "aws_secret_access_key")
(aget source-creds-section "aws_secret_access_key")))
;; I'm not sure if this is in the actual standard
(region (or (aget base-config-section "region")
(aget source-config-section "region"))))

(let ((section (with-slots (config-path profile) shared-config
(when (and config-path
(probe-file config-path))
(read-from-file config-path
:profile profile
:allow-no-profile t)))))
(let ((region (aget section "region")))
(when region
(setf (shared-config-region shared-config) region))))
(when (and credential-source (not (member credential-source '("Environment" "Ec2InstanceMetadata" "EcsContainer"))))
(error "Invalid credential_source"))

(when (and role-arn
(or source-profile-name credential-source))
(setf (shared-config-assume-role shared-config)
(make-assume-role-config :role-arn role-arn
:source-profile source-profile-name
:credential-source credential-source
:external-id (aget base-config-section "external_id")
:serial-number (aget base-config-section "mfa_serial")
:role-session-name (aget base-config-section "role_session_name"))))
(when (and access-key-id secret-access-key)
(setf (shared-config-credentials shared-config)
(make-credentials
:access-key-id access-key-id
:secret-access-key secret-access-key
:session-token (or (aget base-creds-section "aws_session_token")
(aget source-creds-section "aws_session_token"))
:provider-name (format nil "shared-config: ~A"
(shared-config-config-path shared-config)))))

(when region
(setf (shared-config-region shared-config) region))

shared-config))