mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2026-02-11 10:05:41 +00:00
Compare commits
13 Commits
db-abstrac
...
fix-siteke
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5410a4657b | ||
|
|
7d0e4c6be4 | ||
|
|
85f91cb79b | ||
|
|
31978a83f2 | ||
|
|
22b312b8c5 | ||
|
|
2dce6eb2e8 | ||
|
|
c7d1bc3191 | ||
|
|
37004c7b4f | ||
|
|
fa9a1a2f4c | ||
|
|
5daeffd6fb | ||
|
|
be9c6b757e | ||
|
|
b30bc67bd4 | ||
|
|
a9f8cc24a6 |
16
CHANGELOG.md
16
CHANGELOG.md
@@ -2,4 +2,18 @@
|
|||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Rename pow section in settings to captcha and add options to configure([`42544ec42`](https://github.com/mCaptcha/mCaptcha/commit/42544ec421e0c3ec4a8d132e6101ab4069bf0065))
|
- ([`7d0e4c6`](https://github.com/mCaptcha/mCaptcha/commit/7d0e4c6be4b0769921cda7681858ebe16ec9a07b)) Add `secret` parameter to token verification request payload(`/api/v1/pow/siteverify`) to mitigate a security issue that @gusted found:
|
||||||
|
> ...A malicious user could grab the sitekey
|
||||||
|
> and use that sitekey with mcaptcha to use it for their own server.
|
||||||
|
> While they can now go abuse it for illegal stuff or other stuff.
|
||||||
|
> You might decide, oh I don't want this! and terminate a legitimate
|
||||||
|
> siteKey.
|
||||||
|
> New request payload:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"secret": "<your-users-secret>", // found in /settings in the dashbaord
|
||||||
|
"token": "<token-presented-by-the-user>",
|
||||||
|
"key": "<your-sitekey>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- ([`42544ec42`](https://github.com/mCaptcha/mCaptcha/commit/42544ec421e0c3ec4a8d132e6101ab4069bf0065)) Rename pow section in settings to captcha and add options to configure
|
||||||
|
|||||||
419
Cargo.lock
generated
419
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
35
Dockerfile
35
Dockerfile
@@ -17,23 +17,32 @@ COPY Makefile /src/
|
|||||||
COPY scripts /src/scripts
|
COPY scripts /src/scripts
|
||||||
RUN make frontend
|
RUN make frontend
|
||||||
|
|
||||||
|
FROM rust:latest as planner
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
WORKDIR /src
|
||||||
|
COPY . /src/
|
||||||
|
RUN cargo chef prepare --recipe-path recipe.json
|
||||||
|
|
||||||
|
FROM rust:latest as cacher
|
||||||
|
WORKDIR /src/
|
||||||
|
RUN cargo install cargo-chef
|
||||||
|
COPY --from=planner /src/recipe.json recipe.json
|
||||||
|
RUN cargo chef cook --release --recipe-path recipe.json
|
||||||
|
|
||||||
FROM rust:latest as rust
|
FROM rust:latest as rust
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
RUN mkdir src && echo "fn main() {}" > src/main.rs
|
COPY . .
|
||||||
COPY Cargo.toml .
|
COPY --from=cacher /src/target target
|
||||||
RUN sed -i '/.*build.rs.*/d' Cargo.toml
|
#COPY --from=cacher /src/db/db-core/target /src/db/db-core/target
|
||||||
COPY Cargo.lock .
|
#COPY --from=cacher /src/db/db-sqlx-postgres/target /src/db/db-sqlx-postgres/target
|
||||||
COPY migrations /src/migrations
|
#COPY --from=cacher /src/db/db-migrations/target /src/db/db-migrations/target
|
||||||
COPY sqlx-data.json /src/
|
#COPY --from=cacher /src/utils/cache-bust/target /src/utils/cache-bust/target
|
||||||
COPY src/tests-migrate.rs /src/src/tests-migrate.rs
|
|
||||||
COPY src/settings.rs /src/src/settings.rs
|
|
||||||
RUN cargo --version
|
|
||||||
RUN cargo build --release
|
|
||||||
COPY . /src
|
|
||||||
COPY --from=frontend /src/static/cache/bundle/ /src/static/cache/bundle/
|
COPY --from=frontend /src/static/cache/bundle/ /src/static/cache/bundle/
|
||||||
RUN cargo build --release
|
RUN cargo --version
|
||||||
|
RUN make cache-bust
|
||||||
|
RUN cargo build --release
|
||||||
|
|
||||||
FROM debian:bullseye
|
FROM debian:bullseye as mCaptcha
|
||||||
LABEL org.opencontainers.image.source https://github.com/mCaptcha/mCaptcha
|
LABEL org.opencontainers.image.source https://github.com/mCaptcha/mCaptcha
|
||||||
RUN useradd -ms /bin/bash -u 1001 mcaptcha
|
RUN useradd -ms /bin/bash -u 1001 mcaptcha
|
||||||
WORKDIR /home/mcaptcha
|
WORKDIR /home/mcaptcha
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ Clone the repo and run the following from the root of the repo:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/mCaptcha/mCaptcha.git
|
git clone https://github.com/mCaptcha/mCaptcha.git
|
||||||
docker-compose -d up
|
docker-compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
After the containers are up, visit [http://localhost:7000](http://localhost:7000) and login with the default credentials:
|
After the containers are up, visit [http://localhost:7000](http://localhost:7000) and login with the default credentials:
|
||||||
@@ -115,7 +115,7 @@ After the containers are up, visit [http://localhost:7000](http://localhost:7000
|
|||||||
|
|
||||||
It takes a while to build the image so please be patient :)
|
It takes a while to build the image so please be patient :)
|
||||||
|
|
||||||
See [DEPLOYMENT.md](./docs/DEPLOYMET.md) detailed alternate deployment
|
See [DEPLOYMENT.md](./docs/DEPLOYMENT.md) detailed alternate deployment
|
||||||
methods.
|
methods.
|
||||||
|
|
||||||
## Development:
|
## Development:
|
||||||
@@ -124,7 +124,7 @@ See [HACKING.md](./docs/HACKING.md)
|
|||||||
|
|
||||||
## Deployment:
|
## Deployment:
|
||||||
|
|
||||||
See [DEPLOYMENT.md](./docs/DEPLOYMET.md)
|
See [DEPLOYMENT.md](./docs/DEPLOYMENT.md)
|
||||||
|
|
||||||
## Configuration:
|
## Configuration:
|
||||||
|
|
||||||
|
|||||||
@@ -134,6 +134,9 @@ pub trait MCDatabase: std::marker::Send + std::marker::Sync + CloneSPDatabase {
|
|||||||
/// get a user's secret
|
/// get a user's secret
|
||||||
async fn get_secret(&self, username: &str) -> DBResult<Secret>;
|
async fn get_secret(&self, username: &str) -> DBResult<Secret>;
|
||||||
|
|
||||||
|
/// get a user's secret from a captcha key
|
||||||
|
async fn get_secret_from_captcha(&self, key: &str) -> DBResult<Secret>;
|
||||||
|
|
||||||
/// update a user's secret
|
/// update a user's secret
|
||||||
async fn update_secret(&self, username: &str, secret: &str) -> DBResult<()>;
|
async fn update_secret(&self, username: &str, secret: &str) -> DBResult<()>;
|
||||||
|
|
||||||
|
|||||||
@@ -176,6 +176,10 @@ pub async fn database_works<'a, T: MCDatabase>(
|
|||||||
assert!(db.captcha_exists(None, c.key).await.unwrap());
|
assert!(db.captcha_exists(None, c.key).await.unwrap());
|
||||||
assert!(db.captcha_exists(Some(p.username), c.key).await.unwrap());
|
assert!(db.captcha_exists(Some(p.username), c.key).await.unwrap());
|
||||||
|
|
||||||
|
// get secret from captcha key
|
||||||
|
let secret_from_captcha = db.get_secret_from_captcha(&c.key).await.unwrap();
|
||||||
|
assert_eq!(secret_from_captcha.secret, p.secret, "user secret matches");
|
||||||
|
|
||||||
// get captcha configuration
|
// get captcha configuration
|
||||||
let captcha = db.get_captcha_config(p.username, c.key).await.unwrap();
|
let captcha = db.get_captcha_config(p.username, c.key).await.unwrap();
|
||||||
assert_eq!(captcha.key, c.key);
|
assert_eq!(captcha.key, c.key);
|
||||||
|
|||||||
@@ -14,10 +14,13 @@
|
|||||||
* You should have received a copy of the GNU Affero General Public License
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
use db_core::dev::*;
|
use db_core::dev::*;
|
||||||
|
|
||||||
use sqlx::postgres::PgPoolOptions;
|
use sqlx::postgres::PgPoolOptions;
|
||||||
use sqlx::types::time::OffsetDateTime;
|
use sqlx::types::time::OffsetDateTime;
|
||||||
|
use sqlx::ConnectOptions;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
@@ -42,6 +45,7 @@ pub enum ConnectionOptions {
|
|||||||
|
|
||||||
pub struct Fresh {
|
pub struct Fresh {
|
||||||
pub pool_options: PgPoolOptions,
|
pub pool_options: PgPoolOptions,
|
||||||
|
pub disable_logging: bool,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +67,22 @@ impl Connect for ConnectionOptions {
|
|||||||
type Pool = Database;
|
type Pool = Database;
|
||||||
async fn connect(self) -> DBResult<Self::Pool> {
|
async fn connect(self) -> DBResult<Self::Pool> {
|
||||||
let pool = match self {
|
let pool = match self {
|
||||||
Self::Fresh(fresh) => fresh
|
Self::Fresh(fresh) => {
|
||||||
.pool_options
|
let mut connect_options =
|
||||||
.connect(&fresh.url)
|
sqlx::postgres::PgConnectOptions::from_str(&fresh.url).unwrap();
|
||||||
.await
|
if fresh.disable_logging {
|
||||||
.map_err(|e| DBError::DBError(Box::new(e)))?,
|
connect_options.disable_statement_logging();
|
||||||
|
}
|
||||||
|
sqlx::postgres::PgConnectOptions::from_str(&fresh.url)
|
||||||
|
.unwrap()
|
||||||
|
.disable_statement_logging();
|
||||||
|
fresh
|
||||||
|
.pool_options
|
||||||
|
.connect_with(connect_options)
|
||||||
|
.await
|
||||||
|
.map_err(|e| DBError::DBError(Box::new(e)))?
|
||||||
|
}
|
||||||
|
|
||||||
Self::Existing(conn) => conn.0,
|
Self::Existing(conn) => conn.0,
|
||||||
};
|
};
|
||||||
Ok(Database { pool })
|
Ok(Database { pool })
|
||||||
@@ -284,6 +299,22 @@ impl MCDatabase for Database {
|
|||||||
Ok(secret)
|
Ok(secret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get a user's secret from a captcha key
|
||||||
|
async fn get_secret_from_captcha(&self, key: &str) -> DBResult<Secret> {
|
||||||
|
let secret = sqlx::query_as!(
|
||||||
|
Secret,
|
||||||
|
r#"SELECT secret FROM mcaptcha_users WHERE ID = (
|
||||||
|
SELECT user_id FROM mcaptcha_config WHERE key = $1
|
||||||
|
)"#,
|
||||||
|
key,
|
||||||
|
)
|
||||||
|
.fetch_one(&self.pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| map_row_not_found_err(e, DBError::AccountNotFound))?;
|
||||||
|
|
||||||
|
Ok(secret)
|
||||||
|
}
|
||||||
|
|
||||||
/// update a user's secret
|
/// update a user's secret
|
||||||
async fn update_secret(&self, username: &str, secret: &str) -> DBResult<()> {
|
async fn update_secret(&self, username: &str, secret: &str) -> DBResult<()> {
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
|
|||||||
@@ -68,7 +68,11 @@ async fn everyting_works() {
|
|||||||
|
|
||||||
let url = env::var("POSTGRES_DATABASE_URL").unwrap();
|
let url = env::var("POSTGRES_DATABASE_URL").unwrap();
|
||||||
let pool_options = PgPoolOptions::new().max_connections(2);
|
let pool_options = PgPoolOptions::new().max_connections(2);
|
||||||
let connection_options = ConnectionOptions::Fresh(Fresh { pool_options, url });
|
let connection_options = ConnectionOptions::Fresh(Fresh {
|
||||||
|
pool_options,
|
||||||
|
url,
|
||||||
|
disable_logging: false,
|
||||||
|
});
|
||||||
let db = connection_options.connect().await.unwrap();
|
let db = connection_options.connect().await.unwrap();
|
||||||
|
|
||||||
db.migrate().await.unwrap();
|
db.migrate().await.unwrap();
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
version: '3.9'
|
version: "3.9"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
mcaptcha:
|
mcaptcha:
|
||||||
@@ -6,11 +6,15 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 7000:7000
|
- 7000:7000
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: postgres://postgres:password@postgres:5432/postgres # set password at placeholder
|
DATABASE_URL: postgres://postgres:password@mcaptcha_postgres:5432/postgres # set password at placeholder
|
||||||
MCAPTCHA_REDIS_URL: redis://mcaptcha-redis/
|
MCAPTCHA_REDIS_URL: redis://mcaptcha-redis/
|
||||||
RUST_LOG: debug
|
RUST_LOG: debug
|
||||||
|
PORT: 7000
|
||||||
|
depends_on:
|
||||||
|
- mcaptcha-postgres
|
||||||
|
- mcaptcha-redis
|
||||||
|
|
||||||
postgres:
|
mcaptcha_postgres:
|
||||||
image: postgres:13.2
|
image: postgres:13.2
|
||||||
volumes:
|
volumes:
|
||||||
- mcaptcha-data:/var/lib/postgresql/
|
- mcaptcha-data:/var/lib/postgresql/
|
||||||
|
|||||||
@@ -34,14 +34,14 @@ docker run -p <host-machine-port>:<port-in-configuration-file> \
|
|||||||
|
|
||||||
If you don't have a Postgres instance running, you can either install
|
If you don't have a Postgres instance running, you can either install
|
||||||
one using a package manager or launch one with docker. A [docker-compose
|
one using a package manager or launch one with docker. A [docker-compose
|
||||||
configuration]('../docker-compose.yml) is available that will launch both
|
configuration](../docker-compose.yml) is available that will launch both
|
||||||
a database instance mcaptcha instance.
|
a database instance mcaptcha instance.
|
||||||
|
|
||||||
## With docker-compose
|
## With docker-compose
|
||||||
|
|
||||||
1. Follow steps above to build docker image.
|
1. Follow steps above to build docker image.
|
||||||
|
|
||||||
2. Set database password [docker-compose configuration]('../docker-compose.yml).
|
2. Set database password [docker-compose configuration](../docker-compose.yml).
|
||||||
|
|
||||||
3. Launch network:
|
3. Launch network:
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ refer to [official instructions](https://www.gnu.org/software/make/)
|
|||||||
|
|
||||||
### External Dependencies:
|
### External Dependencies:
|
||||||
|
|
||||||
### Postgres databse:
|
### Postgres database:
|
||||||
|
|
||||||
The backend requires a Postgres database. We have
|
The backend requires a Postgres database. We have
|
||||||
compiletime SQL checks so without a database available, you won't be
|
compiletime SQL checks so without a database available, you won't be
|
||||||
@@ -125,7 +125,7 @@ $ make
|
|||||||
default Run app in debug mode
|
default Run app in debug mode
|
||||||
clean Delete build artifacts
|
clean Delete build artifacts
|
||||||
coverage Generate code coverage report in HTML format
|
coverage Generate code coverage report in HTML format
|
||||||
dev-env Setup development environtment
|
dev-env Setup development environment
|
||||||
doc Generate documentation
|
doc Generate documentation
|
||||||
docker Build Docker image
|
docker Build Docker image
|
||||||
docker-publish Build and publish Docker image
|
docker-publish Build and publish Docker image
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ pub async fn get_config(
|
|||||||
///
|
///
|
||||||
/// This fn gets mcaptcha config from database, builds [Defense][libmcaptcha::Defense],
|
/// This fn gets mcaptcha config from database, builds [Defense][libmcaptcha::Defense],
|
||||||
/// creates [MCaptcha][libmcaptcha::MCaptcha] and adds it to [Master][libmcaptcha::Defense]
|
/// creates [MCaptcha][libmcaptcha::MCaptcha] and adds it to [Master][libmcaptcha::Defense]
|
||||||
async fn init_mcaptcha(data: &AppData, key: &str) -> ServiceResult<()> {
|
pub async fn init_mcaptcha(data: &AppData, key: &str) -> ServiceResult<()> {
|
||||||
|
println!("Initializing captcha");
|
||||||
// get levels
|
// get levels
|
||||||
let levels = data.db.get_captcha_levels(None, key).await?;
|
let levels = data.db.get_captcha_levels(None, key).await?;
|
||||||
let duration = data.db.get_captcha_cooldown(key).await?;
|
let duration = data.db.get_captcha_cooldown(key).await?;
|
||||||
@@ -117,6 +118,7 @@ async fn init_mcaptcha(data: &AppData, key: &str) -> ServiceResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let defense = defense.build()?;
|
let defense = defense.build()?;
|
||||||
|
println!("{:?}", defense);
|
||||||
|
|
||||||
// create captcha
|
// create captcha
|
||||||
let mcaptcha = MCaptchaBuilder::default()
|
let mcaptcha = MCaptchaBuilder::default()
|
||||||
@@ -182,4 +184,98 @@ pub mod tests {
|
|||||||
let config: PoWConfig = test::read_body_json(get_config_resp).await;
|
let config: PoWConfig = test::read_body_json(get_config_resp).await;
|
||||||
assert_eq!(config.difficulty_factor, L1.difficulty_factor);
|
assert_eq!(config.difficulty_factor, L1.difficulty_factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
pub async fn pow_difficulty_factor_increases_on_visitor_count_increase() {
|
||||||
|
use super::*;
|
||||||
|
use crate::tests::*;
|
||||||
|
use crate::*;
|
||||||
|
use actix_web::test;
|
||||||
|
|
||||||
|
use libmcaptcha::defense::Level;
|
||||||
|
|
||||||
|
use crate::api::v1::mcaptcha::create::CreateCaptcha;
|
||||||
|
use crate::api::v1::mcaptcha::create::MCaptchaDetails;
|
||||||
|
|
||||||
|
const NAME: &str = "powusrworks2";
|
||||||
|
const PASSWORD: &str = "testingpas";
|
||||||
|
const EMAIL: &str = "randomuser2@a.com";
|
||||||
|
pub const L1: Level = Level {
|
||||||
|
difficulty_factor: 10,
|
||||||
|
visitor_threshold: 10,
|
||||||
|
};
|
||||||
|
pub const L2: Level = Level {
|
||||||
|
difficulty_factor: 20,
|
||||||
|
visitor_threshold: 20,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const L3: Level = Level {
|
||||||
|
difficulty_factor: 30,
|
||||||
|
visitor_threshold: 30,
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = get_data().await;
|
||||||
|
let data = &data;
|
||||||
|
let levels = [L1, L2, L3];
|
||||||
|
|
||||||
|
delete_user(data, NAME).await;
|
||||||
|
|
||||||
|
let (_, signin_resp) = register_and_signin(data, NAME, EMAIL, PASSWORD).await;
|
||||||
|
let cookies = get_cookie!(signin_resp);
|
||||||
|
let app = get_app!(data).await;
|
||||||
|
|
||||||
|
let create_captcha = CreateCaptcha {
|
||||||
|
levels: levels.into(),
|
||||||
|
duration: 30,
|
||||||
|
description: "dummy".into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 1. add level
|
||||||
|
let add_token_resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&create_captcha, V1_API_ROUTES.captcha.create)
|
||||||
|
.cookie(cookies.clone())
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(add_token_resp.status(), StatusCode::OK);
|
||||||
|
let token_key: MCaptchaDetails = test::read_body_json(add_token_resp).await;
|
||||||
|
|
||||||
|
let get_config_payload = GetConfigPayload {
|
||||||
|
key: token_key.key.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let url = V1_API_ROUTES.pow.get_config;
|
||||||
|
let mut prev = 0;
|
||||||
|
for (count, l) in levels.iter().enumerate() {
|
||||||
|
for l in prev..l.visitor_threshold * 2 {
|
||||||
|
let get_config_resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&get_config_payload, V1_API_ROUTES.pow.get_config)
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let get_config_resp = test::call_service(
|
||||||
|
&app,
|
||||||
|
post_request!(&get_config_payload, V1_API_ROUTES.pow.get_config)
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let config: PoWConfig = test::read_body_json(get_config_resp).await;
|
||||||
|
println!(
|
||||||
|
"[{count}] received difficulty_factor: {} prev difficulty_factor {}",
|
||||||
|
config.difficulty_factor, prev
|
||||||
|
);
|
||||||
|
if count == levels.len() - 1 {
|
||||||
|
assert!(config.difficulty_factor == prev);
|
||||||
|
} else {
|
||||||
|
assert!(config.difficulty_factor > prev);
|
||||||
|
}
|
||||||
|
prev = config.difficulty_factor;
|
||||||
|
}
|
||||||
|
// update and check changes
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,23 +29,41 @@ pub struct CaptchaValidateResp {
|
|||||||
pub valid: bool,
|
pub valid: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
|
pub struct VerifyCaptchaResultPayload {
|
||||||
|
pub secret: String,
|
||||||
|
pub key: String,
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<VerifyCaptchaResultPayload> for VerifyCaptchaResult {
|
||||||
|
fn from(m: VerifyCaptchaResultPayload) -> Self {
|
||||||
|
VerifyCaptchaResult {
|
||||||
|
token: m.token,
|
||||||
|
key: m.key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// API keys are mcaptcha actor names
|
// API keys are mcaptcha actor names
|
||||||
|
|
||||||
/// route hander that validates a PoW solution token
|
/// route hander that validates a PoW solution token
|
||||||
#[my_codegen::post(path = "V1_API_ROUTES.pow.validate_captcha_token()")]
|
#[my_codegen::post(path = "V1_API_ROUTES.pow.validate_captcha_token()")]
|
||||||
pub async fn validate_captcha_token(
|
pub async fn validate_captcha_token(
|
||||||
payload: web::Json<VerifyCaptchaResult>,
|
payload: web::Json<VerifyCaptchaResultPayload>,
|
||||||
data: AppData,
|
data: AppData,
|
||||||
) -> ServiceResult<impl Responder> {
|
) -> ServiceResult<impl Responder> {
|
||||||
|
let secret = data.db.get_secret_from_captcha(&payload.key).await?;
|
||||||
|
if secret.secret != payload.secret {
|
||||||
|
return Err(ServiceError::WrongPassword);
|
||||||
|
}
|
||||||
|
let payload: VerifyCaptchaResult = payload.into_inner().into();
|
||||||
let key = payload.key.clone();
|
let key = payload.key.clone();
|
||||||
let res = data
|
let res = data.captcha.validate_verification_tokens(payload).await?;
|
||||||
.captcha
|
let resp = CaptchaValidateResp { valid: res };
|
||||||
.validate_verification_tokens(payload.into_inner())
|
|
||||||
.await?;
|
|
||||||
let payload = CaptchaValidateResp { valid: res };
|
|
||||||
data.stats.record_confirm(&data, &key).await?;
|
data.stats.record_confirm(&data, &key).await?;
|
||||||
//println!("{:?}", &payload);
|
//println!("{:?}", &payload);
|
||||||
Ok(HttpResponse::Ok().json(payload))
|
Ok(HttpResponse::Ok().json(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -76,8 +94,21 @@ pub mod tests {
|
|||||||
delete_user(data, NAME).await;
|
delete_user(data, NAME).await;
|
||||||
|
|
||||||
register_and_signin(data, NAME, EMAIL, PASSWORD).await;
|
register_and_signin(data, NAME, EMAIL, PASSWORD).await;
|
||||||
let (_, _signin_resp, token_key) = add_levels_util(data, NAME, PASSWORD).await;
|
let (_, signin_resp, token_key) = add_levels_util(data, NAME, PASSWORD).await;
|
||||||
let app = get_app!(data).await;
|
let app = get_app!(data).await;
|
||||||
|
let cookies = get_cookie!(signin_resp);
|
||||||
|
|
||||||
|
let secret = test::call_service(
|
||||||
|
&app,
|
||||||
|
test::TestRequest::get()
|
||||||
|
.cookie(cookies.clone())
|
||||||
|
.uri(V1_API_ROUTES.account.get_secret)
|
||||||
|
.to_request(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
assert_eq!(secret.status(), StatusCode::OK);
|
||||||
|
let secret: db_core::Secret = test::read_body_json(secret).await;
|
||||||
|
let secret = secret.secret;
|
||||||
|
|
||||||
let get_config_payload = GetConfigPayload {
|
let get_config_payload = GetConfigPayload {
|
||||||
key: token_key.key.clone(),
|
key: token_key.key.clone(),
|
||||||
@@ -116,11 +147,35 @@ pub mod tests {
|
|||||||
assert_eq!(pow_verify_resp.status(), StatusCode::OK);
|
assert_eq!(pow_verify_resp.status(), StatusCode::OK);
|
||||||
let client_token: ValidationToken = test::read_body_json(pow_verify_resp).await;
|
let client_token: ValidationToken = test::read_body_json(pow_verify_resp).await;
|
||||||
|
|
||||||
let validate_payload = VerifyCaptchaResult {
|
let mut validate_payload = VerifyCaptchaResultPayload {
|
||||||
token: client_token.token.clone(),
|
token: client_token.token.clone(),
|
||||||
key: token_key.key.clone(),
|
key: token_key.key.clone(),
|
||||||
|
secret: NAME.to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// siteverify authentication failure
|
||||||
|
bad_post_req_test(
|
||||||
|
data,
|
||||||
|
NAME,
|
||||||
|
PASSWORD,
|
||||||
|
VERIFY_TOKEN_URL,
|
||||||
|
&validate_payload,
|
||||||
|
ServiceError::WrongPassword,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
// let validate_client_token = test::call_service(
|
||||||
|
// &app,
|
||||||
|
// post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
|
||||||
|
// )
|
||||||
|
// .await;
|
||||||
|
// assert_eq!(validate_client_token.status(), StatusCode::OK);
|
||||||
|
// let resp: CaptchaValidateResp =
|
||||||
|
// test::read_body_json(validate_client_token).await;
|
||||||
|
// assert!(resp.valid);
|
||||||
|
|
||||||
|
// verifying work
|
||||||
|
validate_payload.secret = secret.clone();
|
||||||
|
|
||||||
let validate_client_token = test::call_service(
|
let validate_client_token = test::call_service(
|
||||||
&app,
|
&app,
|
||||||
post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
|
post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
|
||||||
@@ -139,19 +194,5 @@ pub mod tests {
|
|||||||
.await;
|
.await;
|
||||||
let resp: CaptchaValidateResp = test::read_body_json(string_not_found).await;
|
let resp: CaptchaValidateResp = test::read_body_json(string_not_found).await;
|
||||||
assert!(!resp.valid);
|
assert!(!resp.valid);
|
||||||
|
|
||||||
let validate_payload = VerifyCaptchaResult {
|
|
||||||
token: client_token.token.clone(),
|
|
||||||
key: client_token.token.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// key not found
|
|
||||||
let key_not_found = test::call_service(
|
|
||||||
&app,
|
|
||||||
post_request!(&validate_payload, VERIFY_TOKEN_URL).to_request(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
let resp: CaptchaValidateResp = test::read_body_json(key_not_found).await;
|
|
||||||
assert!(!resp.valid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -190,6 +190,7 @@ impl Data {
|
|||||||
let connection_options = ConnectionOptions::Fresh(Fresh {
|
let connection_options = ConnectionOptions::Fresh(Fresh {
|
||||||
pool_options,
|
pool_options,
|
||||||
url: s.database.url.clone(),
|
url: s.database.url.clone(),
|
||||||
|
disable_logging: !s.debug,
|
||||||
});
|
});
|
||||||
let db = connection_options.connect().await.unwrap();
|
let db = connection_options.connect().await.unwrap();
|
||||||
db.migrate().await.unwrap();
|
db.migrate().await.unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user