stack_epic_api/
auth.rs

1// Copyright 2020 The Grin Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// 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
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::router::{Handler, HandlerObj, ResponseFuture};
16use crate::web::response;
17
18use futures::future::ok;
19use hyper::header::{HeaderValue, AUTHORIZATION, WWW_AUTHENTICATE};
20use hyper::{Body, Request, Response, StatusCode};
21use ring::constant_time::verify_slices_are_equal;
22
23lazy_static! {
24	pub static ref EPIC_BASIC_REALM: HeaderValue =
25		HeaderValue::from_str("Basic realm=EpicAPI").unwrap();
26	pub static ref EPIC_FOREIGN_BASIC_REALM: HeaderValue =
27		HeaderValue::from_str("Basic realm=EpicForeignAPI").unwrap();
28}
29
30// Basic Authentication Middleware
31pub struct BasicAuthMiddleware {
32	api_basic_auth: String,
33	basic_realm: &'static HeaderValue,
34	ignore_uri: Option<String>,
35}
36
37impl BasicAuthMiddleware {
38	pub fn new(
39		api_basic_auth: String,
40		basic_realm: &'static HeaderValue,
41		ignore_uri: Option<String>,
42	) -> BasicAuthMiddleware {
43		BasicAuthMiddleware {
44			api_basic_auth,
45			basic_realm,
46			ignore_uri,
47		}
48	}
49}
50
51impl Handler for BasicAuthMiddleware {
52	fn call(
53		&self,
54		req: Request<Body>,
55		mut handlers: Box<dyn Iterator<Item = HandlerObj>>,
56	) -> ResponseFuture {
57		let next_handler = match handlers.next() {
58			Some(h) => h,
59			None => return response(StatusCode::INTERNAL_SERVER_ERROR, "no handler found"),
60		};
61		if req.method().as_str() == "OPTIONS" {
62			return next_handler.call(req, handlers);
63		}
64		if let Some(u) = self.ignore_uri.as_ref() {
65			if req.uri().path() == u {
66				return next_handler.call(req, handlers);
67			}
68		}
69		if req.headers().contains_key(AUTHORIZATION)
70			&& verify_slices_are_equal(
71				req.headers()[AUTHORIZATION].as_bytes(),
72				&self.api_basic_auth.as_bytes(),
73			)
74			.is_ok()
75		{
76			next_handler.call(req, handlers)
77		} else {
78			// Unauthorized 401
79			unauthorized_response(&self.basic_realm)
80		}
81	}
82}
83
84// Basic Authentication Middleware
85pub struct BasicAuthURIMiddleware {
86	api_basic_auth: String,
87	basic_realm: &'static HeaderValue,
88	target_uri: String,
89}
90
91impl BasicAuthURIMiddleware {
92	pub fn new(
93		api_basic_auth: String,
94		basic_realm: &'static HeaderValue,
95		target_uri: String,
96	) -> BasicAuthURIMiddleware {
97		BasicAuthURIMiddleware {
98			api_basic_auth,
99			basic_realm,
100			target_uri,
101		}
102	}
103}
104
105impl Handler for BasicAuthURIMiddleware {
106	fn call(
107		&self,
108		req: Request<Body>,
109		mut handlers: Box<dyn Iterator<Item = HandlerObj>>,
110	) -> ResponseFuture {
111		let next_handler = match handlers.next() {
112			Some(h) => h,
113			None => return response(StatusCode::INTERNAL_SERVER_ERROR, "no handler found"),
114		};
115		if req.method().as_str() == "OPTIONS" {
116			return next_handler.call(req, handlers);
117		}
118		if req.uri().path() == self.target_uri {
119			if req.headers().contains_key(AUTHORIZATION)
120				&& verify_slices_are_equal(
121					req.headers()[AUTHORIZATION].as_bytes(),
122					&self.api_basic_auth.as_bytes(),
123				)
124				.is_ok()
125			{
126				next_handler.call(req, handlers)
127			} else {
128				// Unauthorized 401
129				unauthorized_response(&self.basic_realm)
130			}
131		} else {
132			next_handler.call(req, handlers)
133		}
134	}
135}
136
137fn unauthorized_response(basic_realm: &HeaderValue) -> ResponseFuture {
138	let response = Response::builder()
139		.status(StatusCode::UNAUTHORIZED)
140		.header(WWW_AUTHENTICATE, basic_realm)
141		.body(Body::empty())
142		.unwrap();
143	Box::pin(ok(response))
144}