Skip to content

Commit 7431c97

Browse files
committed
Add simple python example to get devices
* Later, it will support getting SSH information Signed-off-by: Ryan Friedman <[email protected]>
1 parent e22fd6f commit 7431c97

File tree

3 files changed

+148
-0
lines changed

3 files changed

+148
-0
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Python SSH Connection Config
2+
3+
This application uses `python` to create a SSH connection to a device, and then write an entry in your SSH config file.
4+
5+
## Usage
6+
7+
1. [Set up your Remote.it credentials](https://docs.remote.it/developer-tools/authentication#create-a-remote.it-credentials-file)
8+
1. Install requirements with pip
9+
* `python3 -m pip install -r requirements.txt`
10+
1. Run a query in python
11+
* `./run_query.py --help`
12+
* `./run_query.py`
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
configparser
2+
pyyaml
3+
requests-http-signature==v0.1.0
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#!/usr/bin/env python3
2+
3+
import argparse
4+
from base64 import b64decode
5+
import configparser
6+
import pathlib
7+
import requests
8+
from requests_http_signature import HTTPSignatureAuth
9+
import yaml
10+
11+
DEFAULT_SECTION = "default"
12+
13+
# TODO construct schemas using graphene
14+
QUERY_GET_ALL_DEVICES = "query { login { id email devices { items { id name state}}}}"
15+
16+
17+
def run_query(
18+
credentials: configparser.ConfigParser,
19+
*,
20+
query: str,
21+
section: str = DEFAULT_SECTION,
22+
) -> None:
23+
# Modified from https://docs.remote.it/developer-tools/authentication#api-request-signing
24+
key_id = credentials[section].get("R3_ACCESS_KEY_ID")
25+
key_secret_id = credentials[section].get("R3_SECRET_ACCESS_KEY")
26+
27+
body = {"query": query}
28+
host = "api.remote.it"
29+
url_path = "/graphql/v1"
30+
content_type_header = "application/json"
31+
content_length_header = str(len(body))
32+
33+
headers = {
34+
"host": host,
35+
"path": url_path,
36+
"content-type": content_type_header,
37+
"content-length": content_length_header,
38+
}
39+
40+
# This should work with 0.7.1 but doesn't, likely due to remote.it using an outdated draft standard.
41+
# This seems to have changed behavior in 0.3.0.
42+
# response = requests.post(
43+
# "https://" + host + url_path,
44+
# json=body,
45+
# auth=HTTPSignatureAuth(
46+
# # todo import algorithms from requests_http_signature
47+
# signature_algorithm=algorithms.HMAC_SHA256,
48+
# key=b64decode(key_secret_id),
49+
# key_id=key_id,
50+
# covered_component_ids=[
51+
# "@request-target",
52+
# "host",
53+
# "@date",
54+
# "content-type",
55+
# "content-length"
56+
# ]
57+
# # headers=[
58+
# # "(request-target)",
59+
# # "host",
60+
# # "date",
61+
# # "content-type",
62+
# # "content-length",
63+
# # ],
64+
# ),
65+
# headers=headers,
66+
# )
67+
68+
response = requests.post(
69+
"https://" + host + url_path,
70+
json=body,
71+
auth=HTTPSignatureAuth(
72+
algorithm="hmac-sha256",
73+
key=b64decode(key_secret_id),
74+
key_id=key_id,
75+
headers=[
76+
"(request-target)",
77+
"host",
78+
"date",
79+
"content-type",
80+
"content-length",
81+
],
82+
),
83+
headers=headers,
84+
)
85+
86+
if response.status_code == 200:
87+
print(yaml.dump(response.json()))
88+
else:
89+
print(response.status_code)
90+
print(response.text)
91+
92+
93+
def read_config() -> configparser.ConfigParser:
94+
config_path = pathlib.Path.home() / ".remoteit" / "credentials"
95+
try:
96+
assert config_path.exists()
97+
except AssertionError as e:
98+
print(f"Not found: {config_path}")
99+
raise e
100+
cfg_parser = configparser.ConfigParser()
101+
cfg_parser.read(config_path)
102+
103+
return cfg_parser
104+
105+
106+
def validate_credentials(
107+
credentials: configparser.ConfigParser, *, section=DEFAULT_SECTION
108+
) -> None:
109+
sections = credentials.sections()
110+
# TODO support selecting a section other than default
111+
assert section in sections
112+
cred_section = credentials[section]
113+
assert "R3_ACCESS_KEY_ID" in cred_section
114+
assert "R3_SECRET_ACCESS_KEY" in cred_section
115+
116+
117+
def main() -> None:
118+
parser = argparse.ArgumentParser()
119+
parser.add_argument(
120+
"--section",
121+
type=str,
122+
required=False,
123+
help="Section of credentials to use for auth",
124+
default=DEFAULT_SECTION,
125+
)
126+
args = parser.parse_args()
127+
creds = read_config()
128+
validate_credentials(creds, section=args.section)
129+
run_query(creds, query=QUERY_GET_ALL_DEVICES, section=args.section)
130+
131+
132+
if __name__ == "__main__":
133+
main()

0 commit comments

Comments
 (0)