rustfs_appauth/
token.rs

1// Copyright 2024 RustFS Team
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 rsa::Pkcs1v15Encrypt;
16use rsa::{
17    pkcs8::{DecodePrivateKey, DecodePublicKey}, rand_core::OsRng,
18    RsaPrivateKey,
19    RsaPublicKey,
20};
21use serde::{Deserialize, Serialize};
22use std::io::{Error, Result};
23
24#[derive(Serialize, Deserialize, Debug, Default, Clone)]
25pub struct Token {
26    pub name: String, // Application ID
27    pub expired: u64, // Expiry time (UNIX timestamp)
28}
29
30/// Public key generation Token
31/// [token] Token object
32/// [key] Public key string
33/// Returns the encrypted string processed by base64
34pub fn gencode(token: &Token, key: &str) -> Result<String> {
35    let data = serde_json::to_vec(token)?;
36    let public_key = RsaPublicKey::from_public_key_pem(key).map_err(Error::other)?;
37    let encrypted_data = public_key.encrypt(&mut OsRng, Pkcs1v15Encrypt, &data).map_err(Error::other)?;
38    Ok(base64_simd::URL_SAFE_NO_PAD.encode_to_string(&encrypted_data))
39}
40
41/// Private key resolution Token
42/// [token] Encrypted string processed by base64
43/// [key] Private key string
44/// Return to the Token object
45pub fn parse(token: &str, key: &str) -> Result<Token> {
46    let encrypted_data = base64_simd::URL_SAFE_NO_PAD
47        .decode_to_vec(token.as_bytes())
48        .map_err(Error::other)?;
49    let private_key = RsaPrivateKey::from_pkcs8_pem(key).map_err(Error::other)?;
50    let decrypted_data = private_key.decrypt(Pkcs1v15Encrypt, &encrypted_data).map_err(Error::other)?;
51    let res: Token = serde_json::from_slice(&decrypted_data)?;
52    Ok(res)
53}
54
55pub fn parse_license(license: &str) -> Result<Token> {
56    parse(license, TEST_PRIVATE_KEY)
57    // match parse(license, TEST_PRIVATE_KEY) {
58    //     Ok(token) => {
59    //         if token.expired > SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() {
60    //             Ok(token)
61    //         } else {
62    //             Err("Token expired".into())
63    //         }
64    //     }
65    //     Err(e) => Err(e),
66    // }
67}
68
69static TEST_PRIVATE_KEY: &str = "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCj86SrJIuxSxR6\nBJ/dlJEUIj6NeBRnhLQlCDdovuz61+7kJXVcxaR66w4m8W7SLEUP+IlPtnn6vmiG\n7XMhGNHIr7r1JsEVVLhZmL3tKI66DEZl786ZhG81BWqUlmcooIPS8UEPZNqJXLuz\nVGhxNyVGbj/tV7QC2pSISnKaixc+nrhxvo7w56p5qrm9tik0PjTgfZsUePkoBsSN\npoRkAauS14MAzK6HGB75CzG3dZqXUNWSWVocoWtQbZUwFGXyzU01ammsHQDvc2xu\nK1RQpd1qYH5bOWZ0N0aPFwT0r59HztFXg9sbjsnuhO1A7OiUOkc6iGVuJ0wm/9nA\nwZIBqzgjAgMBAAECggEAPMpeSEbotPhNw2BrllE76ec4omPfzPJbiU+em+wPGoNu\nRJHPDnMKJbl6Kd5jZPKdOOrCnxfd6qcnQsBQa/kz7+GYxMV12l7ra+1Cnujm4v0i\nLTHZvPpp8ZLsjeOmpF3AAzsJEJgon74OqtOlVjVIUPEYKvzV9ijt4gsYq0zfdYv0\nhrTMzyrGM4/UvKLsFIBROAfCeWfA7sXLGH8JhrRAyDrtCPzGtyyAmzoHKHtHafcB\nuyPFw/IP8otAgpDk5iiQPNkH0WwzAQIm12oHuNUa66NwUK4WEjXTnDg8KeWLHHNv\nIfN8vdbZchMUpMIvvkr7is315d8f2cHCB5gEO+GWAQKBgQDR/0xNll+FYaiUKCPZ\nvkOCAd3l5mRhsqnjPQ/6Ul1lAyYWpoJSFMrGGn/WKTa/FVFJRTGbBjwP+Mx10bfb\ngUg2GILDTISUh54fp4zngvTi9w4MWGKXrb7I1jPkM3vbJfC/v2fraQ/r7qHPpO2L\nf6ZbGxasIlSvr37KeGoelwcAQQKBgQDH3hmOTS2Hl6D4EXdq5meHKrfeoicGN7m8\noQK7u8iwn1R9zK5nh6IXxBhKYNXNwdCQtBZVRvFjjZ56SZJb7lKqa1BcTsgJfZCy\nnI3Uu4UykrECAH8AVCVqBXUDJmeA2yE+gDAtYEjvhSDHpUfWxoGHr0B/Oqk2Lxc/\npRy1qV5fYwKBgBWSL/hYVf+RhIuTg/s9/BlCr9SJ0g3nGGRrRVTlWQqjRCpXeFOO\nJzYqSq9pFGKUggEQxoOyJEFPwVDo9gXqRcyov+Xn2kaXl7qQr3yoixc1YZALFDWY\nd1ySBEqQr0xXnV9U/gvEgwotPRnjSzNlLWV2ZuHPtPtG/7M0o1H5GZMBAoGAKr3N\nW0gX53o+my4pCnxRQW+aOIsWq1a5aqRIEFudFGBOUkS2Oz+fI1P1GdrRfhnnfzpz\n2DK+plp/vIkFOpGhrf4bBlJ2psjqa7fdANRFLMaAAfyXLDvScHTQTCcnVUAHQPVq\n2BlSH56pnugyj7SNuLV6pnql+wdhAmRN2m9o1h8CgYAbX2juSr4ioXwnYjOUdrIY\n4+ERvHcXdjoJmmPcAm4y5NbSqLXyU0FQmplNMt2A5LlniWVJ9KNdjAQUt60FZw/+\nr76LdxXaHNZghyx0BOs7mtq5unSQXamZ8KixasfhE9uz3ij1jXjG6hafWkS8/68I\nuWbaZqgvy7a9oPHYlKH7Jg==\n-----END PRIVATE KEY-----\n";
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74    use rsa::{
75        pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
76        RsaPrivateKey,
77    };
78    use std::time::{SystemTime, UNIX_EPOCH};
79    #[test]
80    fn test_gencode_and_parse() {
81        let mut rng = OsRng;
82        let bits = 2048;
83        let private_key = RsaPrivateKey::new(&mut rng, bits).expect("Failed to generate private key");
84        let public_key = RsaPublicKey::from(&private_key);
85
86        let private_key_pem = private_key.to_pkcs8_pem(LineEnding::LF).unwrap();
87        let public_key_pem = public_key.to_public_key_pem(LineEnding::LF).unwrap();
88
89        let token = Token {
90            name: "test_app".to_string(),
91            expired: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600, // 1 hour from now
92        };
93
94        let encoded = gencode(&token, &public_key_pem).expect("Failed to encode token");
95
96        let decoded = parse(&encoded, &private_key_pem).expect("Failed to decode token");
97
98        assert_eq!(token.name, decoded.name);
99        assert_eq!(token.expired, decoded.expired);
100    }
101
102    #[test]
103    fn test_parse_invalid_token() {
104        let private_key_pem = RsaPrivateKey::new(&mut OsRng, 2048)
105            .expect("Failed to generate private key")
106            .to_pkcs8_pem(LineEnding::LF)
107            .unwrap();
108
109        let invalid_token = "invalid_base64_token";
110        let result = parse(invalid_token, &private_key_pem);
111
112        assert!(result.is_err());
113    }
114
115    #[test]
116    fn test_gencode_with_invalid_key() {
117        let token = Token {
118            name: "test_app".to_string(),
119            expired: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs() + 3600, // 1 hour from now
120        };
121
122        let invalid_key = "invalid_public_key";
123        let result = gencode(&token, invalid_key);
124
125        assert!(result.is_err());
126    }
127}