Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@
];
};
}
);
) // {
nixosModules = { timeclonk = import ./module.nix; };
};
}

107 changes: 107 additions & 0 deletions module.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
{ config, options, lib, pkgs, ... }:

with lib;

let

cfg = config.services.timeclonk;
opt = options.services.timeclonk;
settingsFormat = pkgs.formats.toml { };

in

{

###### interface
options = {
services.timeclonk = {
enable = mkEnableOption (lib.mdDoc "timeclonk; markdown based multi user zettelkasten");

user = mkOption {
type = types.str;
default = "timeclonk";
example = "timeclonk-user";
description = "linux user account in which to run timeclonk.";
};
group = lib.mkOption {
type = lib.types.str;
default = "timeclonk";
description = lib.mdDoc "linux group under which timeclonk runs.";
};

settings = lib.mkOption {
inherit (settingsFormat) type;
default = ''
ip = '127.0.0.1'
port = 8000
createdirs = true
altmainsite = []
file_tmp_path = './temp'
file_path = './files'

[orgauth_config]
mainsite = 'http://localhost:8000'
appname = 'timeclonk'
emaildomain = 'timeclonk.com'
db = './timeclonk.db'
admin_email = 'admin@admin.admin'
regen_login_tokens = true
email_token_expiration_ms = 86400000
reset_token_expiration_ms = 86400000
invite_token_expiration_ms = 604800000
open_registration = false
send_emails = false
non_admin_invite = true
remote_registration = true
'';
description = ''
timeclonk config.toml file.
'';
};

listenPort = mkOption {
type = types.nullOr types.int;
default = null;
example = 8011;
description = "Listen on a specific IP port.";
};

};
};

###### implementation
config = mkIf cfg.enable {

systemd.services.timeclonk = {
description = "timeclonk";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];

serviceConfig.User = cfg.user;
serviceConfig.Group = cfg.group;

script = ''
cd "/home/${cfg.user}"
mkdir -p timeclonk
cd timeclonk
echo "${cfg.settings}" > config.toml
RUST_LOG=info ${pkgs.timeclonk}/bin/timeclonk-server -c config.toml
'';
};

users.groups = {
${cfg.group} = { };
};

users.users = lib.mkMerge [
(lib.mkIf (cfg.user == "timeclonk") {
${cfg.user} = {
isSystemUser = true;
group = cfg.group;
home = "/home/${cfg.user}";
createHome = true;
};
})
];
};
}
4 changes: 2 additions & 2 deletions server/config.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
ip = "0.0.0.0"
port = 8010
port = 8000
createdirs = false

[orgauth_config]
db = "./timeclonk.db"
mainsite = "http://localhost:8010"
mainsite = "http://localhost:8000"
appname = "TIMECLONK"
emaildomain = "timeclonk.com"
admin_email = "admin@admin.admin"
Expand Down
128 changes: 93 additions & 35 deletions server/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ use config::Config;
use log::{error, info};
use messages::{PublicMessage, ServerResponse, UserMessage};
use orgauth::data::WhatMessage;
use orgauth::endpoints::Callbacks;
use orgauth::util;
use serde_json;
use std::env;
use std::error::Error;
use std::io::stdin;
use std::path::PathBuf;
use std::str::FromStr;
use timer;
Expand Down Expand Up @@ -114,11 +115,7 @@ async fn user(
&item.data,
req.connection_info()
);
let mut cb = Callbacks {
on_new_user: Box::new(sqldata::on_new_user),
on_delete_user: Box::new(sqldata::on_delete_user),
extra_login_data: Box::new(sqldata::extra_login_data_callback),
};
let mut cb = sqldata::timeclonk_callbacks();

match orgauth::endpoints::user_interface(
&session,
Expand Down Expand Up @@ -150,11 +147,7 @@ async fn admin(
&item.data,
req.connection_info()
);
let mut cb = Callbacks {
on_new_user: Box::new(sqldata::on_new_user),
extra_login_data: Box::new(sqldata::extra_login_data_callback),
on_delete_user: Box::new(sqldata::on_delete_user),
};
let mut cb = sqldata::timeclonk_callbacks();
match orgauth::endpoints::admin_interface_check(
&session,
&data.orgauth_config,
Expand Down Expand Up @@ -238,9 +231,9 @@ async fn new_email(data: web::Data<Config>, req: HttpRequest) -> HttpResponse {
fn defcon() -> Config {
let oc = orgauth::data::Config {
db: PathBuf::from("./timeclonk.db"),
mainsite: "http://localhost:8001".to_string(),
mainsite: "http://localhost:8000".to_string(),
appname: "timeclonk".to_string(),
emaildomain: "localhost:8001".to_string(),
emaildomain: "localhost:8000".to_string(),
admin_email: "admin@admin.admin".to_string(),
regen_login_tokens: false,
login_token_expiration_ms: Some(7 * 24 * 60 * 60 * 1000), // 7 days in milliseconds
Expand All @@ -258,20 +251,9 @@ fn defcon() -> Config {
}
}

fn load_config() -> Config {
match orgauth::util::load_string("config.toml") {
Err(e) => {
error!("error loading config.toml: {:?}", e);
defcon()
}
Ok(config_str) => match toml::from_str(config_str.as_str()) {
Ok(c) => c,
Err(e) => {
error!("error loading config.toml: {:?}", e);
defcon()
}
},
}
pub fn load_config(filename: &str) -> Result<Config, Box<dyn Error>> {
info!("loading config: {}", filename);
Ok(toml::from_str(&util::load_string(&filename)?)?)
}

fn main() {
Expand All @@ -287,22 +269,65 @@ async fn err_main() -> Result<(), Box<dyn Error>> {
.version("1.0")
.author("Ben Burdette")
.about("team time clock web server")
// .arg(
// Arg::with_name("export")
// .short("e")
// .long("export")
// .value_name("FILE")
// .help("Export database to json")
// .takes_value(true),
// )
.arg(
Arg::with_name("config")
.short("c")
.long("config")
.value_name("FILE")
.help("specify config file")
.takes_value(true),
)
.arg(
Arg::with_name("export")
.short("e")
.long("export")
Arg::with_name("write_config")
.short("w")
.long("write_config")
.value_name("FILE")
.help("Export database to json")
.help("write default config file")
.takes_value(true),
)
.arg(
Arg::with_name("promote_to_admin")
.short("p")
.long("promote_to_admin")
.value_name("user name")
.help("grant admin privileges to user")
.takes_value(true),
)
.arg(
Arg::with_name("create_admin_user")
.short("a")
.long("create_admin_user")
.value_name("user name")
.help("create new admin user")
.takes_value(true),
)
.get_matches();

// writing a config file?
if let Some(filename) = matches.value_of("write_config") {
util::write_string(filename, toml::to_string_pretty(&defcon())?.as_str())?;
info!("default config written to file: {}", filename);
return Ok(());
}

// specifying a config file? otherwise try to load the default.
let mut config = match matches.value_of("config") {
Some(filename) => load_config(filename)?,
None => load_config("config.toml")?,
};

// are we exporting the DB?
match matches.value_of("export") {
Some(_exportfile) => {
// do that exporting...
let config = load_config();

sqldata::dbinit(
config.orgauth_config.db.as_path(),
config.orgauth_config.login_token_expiration_ms,
Expand All @@ -323,8 +348,6 @@ async fn err_main() -> Result<(), Box<dyn Error>> {

info!("server init!");

let mut config = load_config();

if config.static_path == None {
for (key, value) in env::vars() {
if key == "TIMECLONK_STATIC_PATH" {
Expand All @@ -351,6 +374,41 @@ async fn err_main() -> Result<(), Box<dyn Error>> {
}
});

// promoting a user to admin?
if let Some(uid) = matches.value_of("promote_to_admin") {
let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?;
let mut user = orgauth::dbfun::read_user_by_name(&conn, uid)?;
user.admin = true;
orgauth::dbfun::update_user(&conn, &user)?;

info!("promoted user {} to admin", uid);
return Ok(());
}

// creating an admin user?
if let Some(username) = matches.value_of("create_admin_user") {
// prompt for password.
println!("Enter password for admin user '{}':", username);
let mut pwd = String::new();
stdin().read_line(&mut pwd)?;
let mut cb = sqldata::timeclonk_callbacks();

let conn = sqldata::connection_open(config.orgauth_config.db.as_path())?;
// make new registration i
let rd = orgauth::data::RegistrationData {
uid: username.to_string(),
pwd: pwd.trim().to_string(),
email: "".to_string(),
};

println!("rd: {:?}", rd);

orgauth::dbfun::new_user(&conn, &rd, None, None, true, None, &mut cb.on_new_user)?;

println!("admin user created: {}", username);
return Ok(());
}

let c = config.clone();
HttpServer::new(move || {
let staticpath = c.static_path.clone().unwrap_or(PathBuf::from("static/"));
Expand Down
9 changes: 9 additions & 0 deletions server/src/sqldata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@ use crate::migrations as tm;
use barrel::backend::Sqlite;
use log::info;
use orgauth::data::RegistrationData;
use orgauth::endpoints::Callbacks;
use orgauth::util::now;
use rusqlite::{params, Connection};
use std::path::Path;
use std::str::FromStr;
use std::time::Duration;

pub fn timeclonk_callbacks() -> Callbacks {
Callbacks {
on_new_user: Box::new(on_new_user),
extra_login_data: Box::new(extra_login_data_callback),
on_delete_user: Box::new(on_delete_user),
}
}

pub fn on_new_user(
conn: &Connection,
_rd: &RegistrationData,
Expand Down