// This is a roxen module. // Copyright © 2005, Anders Lindahl // (basically minor modifications to the example user database) // This module is open source software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2, or // (at your option) any later version. #include // Some defines for the translation system // //LOCALE #define LOCALE(X,Y) _DEF_LOCALE("mod_auth",X,Y) // end of the locale related stuff inherit UserDB; inherit "module"; //! A user database module should inherit @[UserDB]. //! //! Which those are will be indicated in the documentation for those //! functions below. Also, please note that a userdb module has to //! have the thread_safe flag set to 1. constant name = "fake_userdb"; //! The name of the userdatbase, used to identify it from modules or //! htaccess files or similar that wants to authenticate against a //! specific database instead of against any of them. //! //! The name should be reasonably short and should be unique, however, //! nothing will break if more than one database has the same name, it //! will be impossible to know which of them will be used when //! authentication is done, however.. constant cvs_version="$Id: fake_userdb.pike,v 1.5 2005/09/02 12:26:34 lindahl Exp $"; LocaleString module_name = LOCALE(1,"Authentication: Fake user database"); LocaleString module_doc = LOCALE(2,"Module for creating a fake user database from then contents of " "(one or many) filesystem directories. The name of subdirectories in " "these directories will be converted to lowercase and used as " "usernames, and the subdirectories themselves will be regarded the " "home directory of that user." "

" "If the same username occurs in several directories, the first one " "will be used." "

" "Copyright © 2005, by " "Anders Lindahl, Seco Tools AB." "

" "This module is open source software; you can redistribute it and/or " "modify it under the terms of the GNU General Public License as " "published by the Free Software Foundation; either version 2, or (at " "your option) any later version."); /* LOCALE(2,"This module creates a fake user database from the contents of " "filesystem directories, assuming they consists of user home directories." " Case does not matter in the filesystem, usernames will be converted to lower case.

" "If the same username shows up in several directories, the first one is used."); */ static mapping(string:string) name2dir = ([]); static int counter = 0; static void create() { defvar("paths", Variable.StringList(({"/home/"}), VAR_INITIAL, LOCALE(3,"Home directories"), LOCALE(4,"Path to home diretory roots in the real filesystem."))); defvar("includes", Variable.StringList(({".*"}), VAR_INITIAL, LOCALE(3,"Include users"), LOCALE(4,"Regexps of users to include."))); defvar("excludes", Variable.StringList(({"root"}), VAR_INITIAL, LOCALE(3,"Exclude users"), LOCALE(4,"Regexps of users to exclude. This has priority over includes."))); } class SecoUser //! Each user in the new userdatabase system is represented by a user //! object. { static string id; static string pw; static string path; static int usernum; // Passed when this object is created. This is not really enough to // implement all functions below, but it's enough to fulfill the // minimum implementation. inherit User; //! This inherit includes prototypes and some implementations of //! functions this object has to implement. The default //! implementations, if any, are noted below. static void create( string _id, string _pw, string _path ) // Set the variables from the constructor arguments and call the // create method in the parent class. { id = _id; pw = _pw; path = _path; usernum = counter; counter = counter + 1; ::create( this_module() ); } string name() //! The name of the user. This is the short name that is used to log //! in. { return id; } string real_name() //! Return the real name of the user. // Since we do not have a real name in this module, the short name // is returned instead. { return id; } string homedir() { return path; } string crypted_password() //! Used by compat_userinfo(). The default implementation returns //! "x". You do not really have to implement this function. { return "x"; } int uid() //! A numerical UID, or -1 if not applicable { return -1; } int gid() //! A numerical GID, or -1 if not applicable { return -1; } string shell() //! The shell, or 0 if not applicable { return 0; } string gecos() //! The gecos field, the default implementation returns the real //! name. { return real_name(); } int password_authenticate(string password) //! Return 1 if the password is correct, 0 otherwise. The default //! implementation uses the crypted_password() method. // Since our password is in clear text, simply compare it with the // specified one. { return 0; } array(string) groups() { // We implement no groups. return ({}); } } array(string) list_users( RequestID dummy ) //! Return a list of all users handled by this database module. { array(string) tmpusers = ({}); foreach ( query("paths"), string path) { // Check the directory (and update our static list) if (get_dir(path)) { foreach (get_dir(path), string x) { if (Stdio.is_dir(path+x) && good_user(lower_case(x))) { tmpusers = tmpusers + ({ lower_case(x) }); name2dir[lower_case(x)] = x; } } } } return tmpusers; } array(string) list_groups( RequestID id ) //! Return a list of all groups handled by this database module. // No groups for us. { return ({}); } User find_user( string s, RequestID id ) //! Find a user from her name. { // Check if there is a directory for this user. In that case, create a new user object. // Maybe caching this should be useful... Better read up on that. if(good_user(s)) { foreach ( query("paths"), string path) { if(name2dir[s]==0 && get_dir(path)) { // Re-check the path if unknown name. foreach (get_dir(path), string x) { if (Stdio.is_dir(path+x) && good_user(lower_case(x))) { name2dir[lower_case(x)] = x; } } } if(Stdio.is_dir(path + name2dir[s])) { return SecoUser(s,"x",path+name2dir[s]); } } } return 0; } int good_user( string name ) // Checks a username against the include/exclude regexps. { // Excludes has priority. foreach(query("excludes"),string pattern) { if (Regexp(pattern)->match(name)) { return 0; } } foreach(query("includes"), string pattern) { if (Regexp(pattern)->match(name)) { return 1; } } return 0; } User find_user_from_uid( int id ) //! Find a user given a UID. The default implementation loops over //! list_users() and checks the uid() of each one. { // Since we do not have any uid:s, simply return 0. return 0; } Group find_group( string group ) //! Find a group object given a group name. //! The default implementation returns 0. { // This is easy in this module, since we only have one group. :-) return 0; } Group find_group_from_gid( int id ) //! Find a group given a GID. The default implementation loops over //! list_groups() and checks the gid() of each one. { // Again, we do not have gid:s either. return 0; }