Skip to content

Commit 81ebb59

Browse files
authored
Merge pull request #2588 from OpenC3/maint/remove-plaintext-pw
Remove plaintext password support for api
2 parents 4f2d26f + 435b45d commit 81ebb59

File tree

16 files changed

+235
-31
lines changed

16 files changed

+235
-31
lines changed

.github/workflows/cli.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ on:
1616

1717
env:
1818
OPENC3_API_PASSWORD: password
19+
OPENC3_API_PORT: 2900
1920

2021
jobs:
2122
openc3-cli:

docs.openc3.com/docs/development/json-api.md

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This document provides the information necessary for external applications to in
1616
The HTTP Authorization request header contains the credentials to authenticate a user agent with a server, usually, but not necessarily, after the server has responded with a 401 Unauthorized status and the WWW-Authenticate header.
1717

1818
```
19-
Authorization: <token/password>
19+
Authorization: <token>
2020
```
2121

2222
## JSON-RPC 2.0
@@ -117,5 +117,16 @@ If developing an interface for the JSON API from another language, the best way
117117
You can also try sending these raw commands from the terminal with a program like `curl`:
118118

119119
```bash
120-
curl -d '{"jsonrpc": "2.0", "method": "tlm", "params": ["INST HEALTH_STATUS TEMP1"], "id": 2, "keyword_params":{"type":"FORMATTED","scope":"DEFAULT"}}' http://localhost:2900/openc3-api/api -H "Authorization: password"
120+
curl -d '{"jsonrpc": "2.0", "method": "tlm", "params": ["INST HEALTH_STATUS TEMP1"], "id": 2, "keyword_params":{"type":"FORMATTED","scope":"DEFAULT"}}' \
121+
-H "Content-Type: application/json" \
122+
-H "Authorization: <token>" \
123+
http://localhost:2900/openc3-api/api
124+
```
125+
126+
Note that you will need a valid session token in the `Authorization` header to access the JSON API. You can retrieve one either by logging in via your browser and copying the value of `localStorage.openc3Token` in the dev tools console, or also with `curl`:
127+
128+
```bash
129+
curl http://localhost:2900/openc3-api/auth/verify \
130+
-H 'Content-Type: application/json' \
131+
-d '{"token": "your-password-here"}'
121132
```

openc3-cosmos-cmd-tlm-api/app/controllers/auth_controller.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,20 @@ def token_exists
3333

3434
def verify
3535
begin
36-
if OpenC3::AuthModel.verify_no_service(params[:token])
36+
if OpenC3::AuthModel.verify_no_service(params[:token], no_password: false)
37+
render :plain => OpenC3::AuthModel.generate_session()
38+
else
39+
head :unauthorized
40+
end
41+
rescue StandardError => e
42+
log_error(e)
43+
render json: { status: 'error', message: e.message, type: e.class }, status: 500
44+
end
45+
end
46+
47+
def verify_service
48+
begin
49+
if OpenC3::AuthModel.verify(params[:token], service_only: true)
3750
render :plain => OpenC3::AuthModel.generate_session()
3851
else
3952
head :unauthorized

openc3-cosmos-cmd-tlm-api/config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@
212212

213213
get "/auth/token-exists" => "auth#token_exists"
214214
post "/auth/verify" => "auth#verify"
215+
post "/auth/verify_service" => "auth#verify_service"
215216
post "/auth/set" => "auth#set"
216217

217218
get "/internal/health" => "internal_health#health"

openc3-cosmos-cmd-tlm-api/spec/rails_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
abort("The Rails environment is running in production mode!") if Rails.env.production?
2929
require 'rspec/rails'
3030
# Add additional requires below this line. Rails is not loaded until this point!
31+
load 'openc3/io/json_rpc.rb' # This is here because we need our as_json to override Rails'
3132

3233
# Requires supporting ruby files with custom matchers and macros, etc, in
3334
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are

openc3-cosmos-cmd-tlm-api/spec/spec_helper.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
ENV['OPENC3_REDIS_EPHEMERAL_HOSTNAME'] = '127.0.0.1'
7474
ENV['OPENC3_REDIS_EPHEMERAL_PORT'] = '6380'
7575
# Set some usernames / passwords
76-
ENV['OPENC3_API_PASSWORD'] = 'openc3'
76+
ENV['OPENC3_API_PASSWORD'] = 'password'
7777
ENV['OPENC3_SERVICE_PASSWORD'] = 'openc3service'
7878
ENV['OPENC3_REDIS_USERNAME'] = 'openc3'
7979
ENV['OPENC3_REDIS_PASSWORD'] = 'openc3password'
@@ -84,6 +84,17 @@
8484

8585
$openc3_scope = ENV['OPENC3_SCOPE']
8686
$openc3_token = ENV['OPENC3_API_PASSWORD']
87+
$openc3_mock_token = 'mock_token'
88+
89+
# Mock the HTTP request for OpenC3Authentication
90+
require 'openc3/utilities/authentication'
91+
OpenC3::OpenC3Authentication.class_eval do
92+
def _make_auth_request(password)
93+
mock_response = Object.new
94+
mock_response.define_singleton_method(:body) { $openc3_mock_token }
95+
mock_response
96+
end
97+
end
8798

8899
def setup_system(targets = ["SYSTEM", "INST", "EMPTY"])
89100
require 'openc3/system'

openc3-cosmos-init/plugins/pnpm-lock.yaml

Lines changed: 98 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

openc3-cosmos-script-runner-api/spec/rails_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
abort("The Rails environment is running in production mode!") if Rails.env.production?
3030
require 'rspec/rails'
3131
# Add additional requires below this line. Rails is not loaded until this point!
32+
load 'openc3/io/json_rpc.rb' # This is here because we need our as_json to override Rails'
3233

3334
# Requires supporting ruby files with custom matchers and macros, etc, in
3435
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are

openc3-cosmos-script-runner-api/spec/spec_helper.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@
8585

8686
$openc3_scope = ENV['OPENC3_SCOPE']
8787
$openc3_token = ENV['OPENC3_API_PASSWORD']
88+
$openc3_mock_token = 'mock_token'
89+
90+
# Mock the HTTP request for OpenC3Authentication
91+
require 'openc3/utilities/authentication'
92+
OpenC3::OpenC3Authentication.class_eval do
93+
def _make_auth_request(password)
94+
mock_response = Object.new
95+
mock_response.define_singleton_method(:body) { $openc3_mock_token }
96+
mock_response
97+
end
98+
end
8899

89100
def mock_redis
90101
require 'redis'

openc3/lib/openc3/models/auth_model.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,20 @@ def self.set?(key = PRIMARY_KEY)
4242
Store.exists(key) == 1
4343
end
4444

45-
def self.verify(token)
45+
# @param no_password [Boolean] enforces use of a session token or service password (default: true)
46+
def self.verify(token, no_password: true, service_only: false)
4647
# Handle a service password - Generally only used by ScriptRunner
4748
# TODO: Replace this with temporary service tokens
4849
service_password = ENV['OPENC3_SERVICE_PASSWORD']
4950
return true if service_password and service_password == token
5051

51-
return verify_no_service(token)
52+
return false if service_only
53+
54+
return verify_no_service(token, no_password: no_password)
5255
end
5356

54-
def self.verify_no_service(token)
57+
# @param no_password [Boolean] enforces use of a session token (default: true)
58+
def self.verify_no_service(token, no_password: true)
5559
return false if token.nil? or token.empty?
5660

5761
time = Time.now
@@ -64,6 +68,8 @@ def self.verify_no_service(token)
6468
@@session_cache_time = time
6569
return true if @@session_cache[token]
6670

71+
return false if no_password
72+
6773
# Check Direct password
6874
@@token_cache = Store.get(PRIMARY_KEY)
6975
@@token_cache_time = time
@@ -78,7 +84,7 @@ def self.set(token, old_token, key = PRIMARY_KEY)
7884

7985
if set?(key)
8086
raise "old_token must not be nil or empty" if old_token.nil? or old_token.empty?
81-
raise "old_token incorrect" unless verify(old_token)
87+
raise "old_token incorrect" unless verify_no_service(old_token, no_password: false)
8288
end
8389
Store.set(key, hash(token))
8490
end
@@ -96,7 +102,7 @@ def self.logout
96102
end
97103

98104
def self.hash(token)
99-
Digest::SHA2.hexdigest token
105+
Digest::SHA256.hexdigest token
100106
end
101107
end
102108
end

0 commit comments

Comments
 (0)