diff --git a/docs/_static/zmstyle.css b/docs/_static/zmstyle.css new file mode 100644 index 000000000..b8f0235f3 --- /dev/null +++ b/docs/_static/zmstyle.css @@ -0,0 +1,3 @@ +img { + border: 1px solid black !important; + } diff --git a/docs/_static/zmstyles.css b/docs/_static/zmstyles.css deleted file mode 100644 index 8f59f3910..000000000 --- a/docs/_static/zmstyles.css +++ /dev/null @@ -1,13 +0,0 @@ -@import url("default.css"); - -div.admonition-note { -border-top: 2px solid red; -border-bottom: 2px solid red; -border-left: 2px solid red; -border-right: 2px solid red; -background-color: #ff6347 -} - -img { - border: 1px solid black !important; -} diff --git a/docs/conf.py b/docs/conf.py index 2d376f03d..109161b24 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,6 +15,9 @@ import sys import os +def setup(app): + app.add_stylesheet('zmstyle.css') + # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. @@ -51,9 +54,9 @@ copyright = u'2014, https://github.com/ZoneMinder/ZoneMinder/graphs/contributors # built documents. # # The short X.Y version. -version = '1.28.1' +#version = '1.28.1' # The full version, including alpha/beta/rc tags. -release = '1.28.1' +#release = '1.28.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -98,14 +101,15 @@ pygments_style = 'sphinx' # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'default' +#html_theme = 'default' +html_theme = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -html_theme_options = { - "stickysidebar": "true" -} +#html_theme_options = { +# "stickysidebar": "true" +#} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] @@ -130,7 +134,7 @@ html_theme_options = { # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] -html_style='zmstyles.css' +#html_style='zmstyles.css' # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/installationguide/ubuntu.rst b/docs/installationguide/ubuntu.rst new file mode 100644 index 000000000..1d5412077 --- /dev/null +++ b/docs/installationguide/ubuntu.rst @@ -0,0 +1,477 @@ +Ubuntu +====== + +.. contents:: + +Easy Way: Install ZoneMinder from a package (Ubuntu 15.x+) +----------------------------------------------------------- +These instructions are for a brand new ubuntu 15.04 system which does not have ZM installed. + +**Step 1**: Make sure we add the correct packages + +:: + + sudo add-apt-repository ppa:iconnor/zoneminder + sudo apt-get update + +if you don't have mysql already installed: + +:: + + sudo apt-get install mysql-server + +This will ask you to set up a master password for the DB (you are asked for the mysql root password when installing mysql server). + +**Step 2**: Install ZoneMinder + +:: + + sudo apt-get install zoneminder + +**Step 3**: Configure the Database + +:: + + sudo mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +You don't really need this, but no harm (needed if you are upgrading) + +:: + + sudo /usr/bin/zmupdate.pl + +**Step 4**: Configure systemd to recognize ZoneMinder and configure Apache correctly: + +:: + + sudo systemctl enable zoneminder + sudo a2enconf zoneminder + sudo a2enmod cgi + sudo chown -R www-data:www-data /usr/share/zoneminder/ + + +We need this for API routing to work: + +:: + + sudo a2enmod rewrite + +This is probably a bug with iconnor's PPA as of Oct 3, 2015 with package 1.28.107. After installing, ``zm.conf`` does not have the right read permissions, so we need to fix that. This may go away in future PPA releases: + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd + +**Step 5**: Edit Timezone in PHP + +:: + + vi /etc/php5/apache2/php.ini + +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see this: http://php.net/manual/en/timezones.php) + +In my case: + +:: + + date.timezone = America/New_York + +**Step 6**: Restart services + +:: + + sudo service apache2 reload + sudo systemctl restart zoneminder + + +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: + +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + + +**Step 8**: If you have changed your DB login/password from zmuser/zmpass, the API won't know about it + +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. + +If you have, go to ``zoneminder/www/api/app/Config`` & Edit ``database.php`` + +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8', + ); + + +You are done. Lets proceed to make sure everything works: + +Making sure ZM and APIs work: + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + + +**Then your APIs are working** + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** your installation is complete + + +Easy Way: Install ZoneMinder from a package (Ubuntu 14.x) +----------------------------------------------------------- +**These instructions are for a brand new ubuntu 14.x system which does not have ZM installed.** + +**Step 1:** Install ZoneMinder + +:: + + sudo add-apt-repository ppa:iconnor/zoneminder + sudo apt-get update + sudo apt-get install zoneminder + +(just press OK for the prompts you get) + +**Step 2:** Set up DB + +:: + + sudo mysql -uroot -p < /usr/share/zoneminder/db/zm_create.sql + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +**Step 3:** Set up Apache + +:: + + sudo a2enconf zoneminder + sudo a2enmod rewrite + sudo a2enmod cgi + +**Step 4:**:Some tweaks that will be needed: + +Edit /etc/init.d/zoneminder: + +add a ``sleep 10`` right after line 25 that reads ``echo -n "Starting $prog:"`` +(The reason we need this sleep is to make sure ZM starts after mysqld starts) + +As of Oct 3 2015, zm.conf is not readable by ZM. This is likely a bug and will go away in the next package + +:: + + sudo chown www-data:www-data /etc/zm/zm.conf + + + +**Step 5**: If you have changed your DB login/password + +If you changed the DB password **after** installing ZM, the APIs will not be able to connect to the DB. + +If you have, go to zoneminder/www/api/app/Config & Edit ``database.php`` + +There is a class there called ``DATABASE_CONFIG`` - change the ``$default`` array to reflect your new details. Example: + +:: + + public $default = array( + 'datasource' => 'Database/Mysql', + 'persistent' => false, + 'host' => 'localhost', + 'login' => 'mynewDBusername', + 'password' => 'mynewDBpassword' + 'database' => 'zm', + 'prefix' => '', + //'encoding' => 'utf8',` + ); + +We also need to install php5-gd (as of 1.28.107, this is not installed) + +:: + + sudo apt-get install php5-gd + + +**Step 6**: Edit Timezone in PHP + +vi /etc/php5/apache2/php.ini +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see [this](http://php.net/manual/en/timezones.php)) + +In my case: + +:: + + date.timezone = America/New_York + + +**Step 7: make sure live streaming works**: Make sure you can view Monitor streams: + +startup ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + + + +restart: + +:: + + sudo service apache2 restart + /etc/init.d/zoneminder restart + +**Step 8**: Making sure ZM and APIs work: (optional - only if you need APIs) + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + +Then your APIs are working + +Make sure you can view Monitor View: +1. Open up ZM, configure your monitors and verify you can view Monitor feeds. +2. If not, open up ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM (you should not need to do this for packages, as this should automatically work) + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** Your installation is complete + + + + +Harder Way: Build Package From Source +------------------------------------------- +(These instructions assume installation from source on a ubuntu 15.x+ system) + +**Step 1:** First make sure you have the needed tools + +:: + + sudo apt-get update + sudo apt-get install cmake git + +**Step 2:** Next up make sure you have all the dependencies + +:: + + sudo apt-get install apache2 mysql-server php5 php5-mysql build-essential libmysqlclient-dev libssl-dev libbz2-dev libpcre3-dev libdbi-perl libarchive-zip-perl libdate-manip-perl libdevice-serialport-perl libmime-perl libpcre3 libwww-perl libdbd-mysql-perl libsys-mmap-perl yasm automake autoconf libjpeg8-dev libjpeg8 apache2 libapache2-mod-php5 php5-cli libphp-serialization-perl libgnutls-dev libjpeg8-dev libavcodec-dev libavformat-dev libswscale-dev libavutil-dev libv4l-dev libtool ffmpeg libnetpbm10-dev libavdevice-dev libmime-lite-perl dh-autoreconf dpatch policykit-1 libpolkit-gobject-1-dev libextutils-pkgconfig-perl libcurl3 libvlc-dev libcurl4-openssl-dev curl php5-gd + +(you are asked for the mysql root password when installing mysql server - put in a password that you'd like). + +**Step 3:** Download ZoneMinder source code and compile+install: + +:: + + git clone https://github.com/ZoneMinder/ZoneMinder.git + cd ZoneMinder/ + git submodule init + git submodule update + cmake . + make + sudo make install + +**Step 4:** Now make sure your symlinks to events and images are set correctly: + +:: + + sudo ./zmlinkcontent.sh + +**Step 5:** Now lets make sure ZM has DB permissions to write to the DB: + +:: + + mysql -uroot -p -e "grant select,insert,update,delete,create,alter,index,lock tables on zm.* to 'zmuser'@localhost identified by 'zmpass';" + +**Step 6:** Now lets create the DB & its tables that ZM needs + +:: + + mysql -uroot -p + Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch + AllowOverride All + Require all granted + + + Alias /zm /usr/local/share/zoneminder/www + + php_flag register_globals off + Options Indexes FollowSymLinks + + DirectoryIndex index.php + + + + + AllowOverride All + + +**Step 11:** Now lets make sure ZM can read/write to the zoneminder directory: + +:: + + sudo chown -R www-data:www-data /usr/local/share/zoneminder/ + + +**Step 12:** Make sure you can view Monitor View + +1. Open up ZM, configure your monitors and verify you can view Monitor feeds +2. If not, open up ZM console in your browser, go to ``Options->Path`` and make sure ``PATH_ZMS`` is set to ``/zm/cgi-bin/nph-zms`` and restart ZM + +**Step 13**: Edit Timezone in PHP + +vi /etc/php5/apache2/php.ini +Look for [Date] and inside it you will see a date.timezone +that is commented. remove the comment and specific your timezone. +Please make sure the timezone is valid (see http://php.net/manual/en/timezones.php) + +In my case: + +:: + + date.timezone = America/New_York + +**Step 14:** Finally, lets make a config change to apache (needed for htaccess overrides to work for APIs) +Edit /etc/apache2/apache2.conf and add this: + +:: + + + AllowOverride All + Require all granted + + +Restart apache + +:: + + sudo service apache2 reload + +You are done. Lets proceed to make sure everything works: + +Making sure ZM and APIs work: + +1. open up a browser and go to ``http://localhost/zm`` - should bring up ZM +2. (OPTIONAL - just for peace of mind) open up a tab and go to ``http://localhost/zm/api`` - should bring up a screen showing CakePHP version with some green color boxes. Green is good. If you see red, or you don't see green, there may be a problem (should not happen). Ignore any warnings in yellow saying "DebugKit" not installed. You don't need it +3. open up a tab in the same browser and go to ``http://localhost/zm/api/host/getVersion.json`` + +If it responds with something like: + +:: + + { + "version": "1.28.107", + "apiversion": "1.28.107.1" + } + +Then your APIs are working + +Make sure ZM and APIs work with security: +1. Enable OPT_AUTH in ZM +2. Log out of ZM in browser +3. Open a NEW tab in the SAME BROWSER (important) and go to ``http://localhost/zm/api/host/getVersion.json`` - should give you "Unauthorized" along with a lot more of text +4. Go to another tab in the SAME BROWSER (important) and log into ZM +5. Repeat step 3 and it should give you the ZM and API version + +**Congrats** your installation is complete + +Suggested changes to MySQL (Optional but recommended) +------------------------------------------------------ +For most of you Zoneminder will run just fine with the default MySQL settings. There are a couple of settings that may, in time, provide beneficial especially if you have a number of cameras and many events with a lot of files. One setting we recommend is the "innodb_file_per_table" This will be a default setting in MySQL 5.6 but should be added in MySQL 5.5 which comes with Ubuntu 14.04. A description can be found here: http://dev.mysql.com/doc/refman/5.5/en/innodb-multiple-tablespaces.html + +To add "innodb_file_per_table" edit the my.cnf file: + +``vi /etc/mysql/my.cnf`` +Under [mysqld] add +``innodb_file_per_table`` + +Save and exit. + +Restart MySQL +``service mysql restart`` + + diff --git a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in index 7fff3d62e..9a065f8cf 100644 --- a/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in +++ b/scripts/ZoneMinder/lib/ZoneMinder/ConfigData.pm.in @@ -357,7 +357,23 @@ our @options = type => $types{boolean}, category => "system", }, - # PP - Google reCaptcha settings + { + name => "ZM_OPT_USE_API", + default => "yes", + description => "Enable ZoneMinder APIs", + help => qqq(" + ZoneMinder now features a new API using which 3rd party + applications can interact with ZoneMinder data. It is + STRONGLY recommended that you enable authentication along + with APIs. Note that the APIs return sensitive data like + Monitor access details which are configured as JSON objects. + Which is why we recommend you enabling authentication, especially + if you are exposing your ZM instance on the Internet. + "), + type => $types{boolean}, + category => "system", + }, +# PP - Google reCaptcha settings { name => "ZM_OPT_USE_GOOG_RECAPTCHA", default => "no", @@ -410,6 +426,7 @@ our @options = type => $types {string}, category => "system", }, + { name => "ZM_DIR_EVENTS", diff --git a/src/zm_remote_camera.cpp b/src/zm_remote_camera.cpp index 608311649..8ac4a51ba 100644 --- a/src/zm_remote_camera.cpp +++ b/src/zm_remote_camera.cpp @@ -71,7 +71,7 @@ void RemoteCamera::Initialise() } mNeedAuth = false; - mAuthenticator = new Authenticator(username,password); + mAuthenticator = new zm::Authenticator(username,password); struct addrinfo hints; memset(&hints, 0, sizeof(hints)); diff --git a/src/zm_remote_camera.h b/src/zm_remote_camera.h index 44c00bf65..7e3ae79a8 100644 --- a/src/zm_remote_camera.h +++ b/src/zm_remote_camera.h @@ -50,7 +50,7 @@ protected: // fill required fields and set needAuth // subsequent requests can set the required authentication header. bool mNeedAuth; - Authenticator* mAuthenticator; + zm::Authenticator* mAuthenticator; protected: struct addrinfo *hp; diff --git a/src/zm_remote_camera_http.cpp b/src/zm_remote_camera_http.cpp index 44004564f..440c24e05 100644 --- a/src/zm_remote_camera_http.cpp +++ b/src/zm_remote_camera_http.cpp @@ -306,7 +306,7 @@ int RemoteCameraHttp::GetResponse() std::string Header = header; mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == AUTH_DIGEST ) { + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { Debug( 2, "Need Digest Authentication" ); request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); @@ -750,7 +750,7 @@ Debug(3, "Need more data buffer %d < content length %d", buffer.size(), content_ Debug(2, "Checking for digest auth in %s", authenticate_header ); mAuthenticator->checkAuthResponse(Header); - if ( mAuthenticator->auth_method() == AUTH_DIGEST ) { + if ( mAuthenticator->auth_method() == zm::AUTH_DIGEST ) { Debug( 2, "Need Digest Authentication" ); request = stringtf( "GET %s HTTP/%s\r\n", path.c_str(), config.http_version ); request += stringtf( "User-Agent: %s/%s\r\n", config.http_ua, ZM_VERSION ); diff --git a/src/zm_rtsp.cpp b/src/zm_rtsp.cpp index 0dbcb7329..7ac80e9b6 100644 --- a/src/zm_rtsp.cpp +++ b/src/zm_rtsp.cpp @@ -203,9 +203,9 @@ RtspThread::RtspThread( int id, RtspMethod method, const std::string &protocol, mNeedAuth = false; StringVector parts = split(auth,":"); if (parts.size() > 1) - mAuthenticator = new Authenticator(parts[0], parts[1]); + mAuthenticator = new zm::Authenticator(parts[0], parts[1]); else - mAuthenticator = new Authenticator(parts[0], ""); + mAuthenticator = new zm::Authenticator(parts[0], ""); } RtspThread::~RtspThread() diff --git a/src/zm_rtsp.h b/src/zm_rtsp.h index acd28e651..192200d0b 100644 --- a/src/zm_rtsp.h +++ b/src/zm_rtsp.h @@ -66,7 +66,7 @@ private: // subsequent requests can set the required authentication header. bool mNeedAuth; int respCode; - Authenticator* mAuthenticator; + zm::Authenticator* mAuthenticator; std::string mHttpSession; ///< Only for RTSP over HTTP sessions diff --git a/src/zm_rtsp_auth.cpp b/src/zm_rtsp_auth.cpp index fd9087afa..10ecf3475 100644 --- a/src/zm_rtsp_auth.cpp +++ b/src/zm_rtsp_auth.cpp @@ -24,6 +24,8 @@ #include #include +namespace zm { + Authenticator::Authenticator(std::string &username, std::string password) { #ifdef HAVE_GCRYPT_H // Special initialisation for libgcrypt @@ -227,3 +229,5 @@ void Authenticator::checkAuthResponse(std::string &response) { Debug( 2, "Didn't find auth line in %s", authLine.c_str()); } } + +} // namespace zm diff --git a/src/zm_rtsp_auth.h b/src/zm_rtsp_auth.h index 23745f9c7..079dec639 100644 --- a/src/zm_rtsp_auth.h +++ b/src/zm_rtsp_auth.h @@ -32,6 +32,8 @@ #include #endif // HAVE_GCRYPT_H || HAVE_LIBCRYPTO +namespace zm { + enum AuthMethod { AUTH_UNDEFINED = 0, AUTH_BASIC = 1, AUTH_DIGEST = 2 }; class Authenticator { public: @@ -62,4 +64,6 @@ private: int nc; }; +} // namespace zm + #endif // ZM_RTSP_AUTH_H diff --git a/web/api/app/Controller/AppController.php b/web/api/app/Controller/AppController.php index 1d85115e8..5b39597b3 100644 --- a/web/api/app/Controller/AppController.php +++ b/web/api/app/Controller/AppController.php @@ -34,7 +34,7 @@ class AppController extends Controller { use CrudControllerTrait; public $components = [ - 'Session', // PP - We are going to use SessionHelper to check PHP session vars + 'Session', // We are going to use SessionHelper to check PHP session vars 'RequestHandler', 'Crud.Crud' => [ 'actions' => [ @@ -49,7 +49,7 @@ class AppController extends Controller { ] ]; - //PP - Global beforeFilter function + // Global beforeFilter function //Zoneminder sets the username session variable // to the logged in user. If this variable is set // then you are logged in @@ -58,14 +58,62 @@ class AppController extends Controller { // Also checking to do this only if ZM_OPT_USE_AUTH is on public function beforeFilter() { $this->loadModel('Config'); + + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_API')); + $config = $this->Config->find('first', $options); + $zmOptApi = $config['Config']['Value']; + + if ($zmOptApi !='1') + { + throw new UnauthorizedException(__('API Disabled')); + return; + } + $options = array('conditions' => array('Config.' . $this->Config->primaryKey => 'ZM_OPT_USE_AUTH')); $config = $this->Config->find('first', $options); $zmOptAuth = $config['Config']['Value']; if (!$this->Session->Read('user.Username') && ($zmOptAuth=='1')) { - throw new NotFoundException(__('Not Authenticated')); + throw new UnauthorizedException(__('Not Authenticated')); return; } + else + { + $this->loadModel('User'); + $loggedinUser = $this->Session->Read('user.Username'); + $isEnabled = $this->Session->Read('user.Enabled'); + // this will likely never happen as if its + // not enabled, login will fail and Not Auth will be returned + // however, keeping this here for now + if ($isEnabled != "1" && $zmOptAuth=="1") + { + throw new UnauthorizedException(__('User is not enabled')); + return; + } + + if ($zmOptAuth=='1') + { + $options = array ('conditions' => array ('User.Username' => $loggedinUser)); + $userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',$userMonitors['User']['MonitorIds']); + $this->Session->Write('streamPermission',$userMonitors['User']['Stream']); + $this->Session->Write('eventPermission',$userMonitors['User']['Events']); + $this->Session->Write('controlPermission',$userMonitors['User']['Control']); + $this->Session->Write('systemPermission',$userMonitors['User']['System']); + $this->Session->Write('monitorPermission',$userMonitors['User']['Monitors']); + } + else // if auth is not on, you can do everything + { + //$userMonitors = $this->User->find('first', $options); + $this->Session->Write('allowedMonitors',''); + $this->Session->Write('streamPermission','View'); + $this->Session->Write('eventPermission','Edit'); + $this->Session->Write('controlPermission','Edit'); + $this->Session->Write('systemPermission','Edit'); + $this->Session->Write('monitorPermission','Edit'); + } + } + } diff --git a/web/api/app/Controller/Component/ImageComponent.php b/web/api/app/Controller/Component/ImageComponent.php index 68a254031..f8ed7a533 100644 --- a/web/api/app/Controller/Component/ImageComponent.php +++ b/web/api/app/Controller/Component/ImageComponent.php @@ -48,7 +48,7 @@ class ImageComponent extends Component { $imageFile = $config['ZM_DIR_EVENTS']."/".$imagePath; //$thumbFile = ZM_DIR_EVENTS."/".$thumbPath; $thumbFile = $thumbPath; - // PP: This segment of code results in errors when trying to get Events API + // This segment of code results in errors when trying to get Events API // This actually seems to be generating images for the angular UI web view // and should not be a part of the API anyway // I've commented it so events APIs continue to work diff --git a/web/api/app/Controller/ConfigsController.php b/web/api/app/Controller/ConfigsController.php index e70978246..48f50ae8e 100644 --- a/web/api/app/Controller/ConfigsController.php +++ b/web/api/app/Controller/ConfigsController.php @@ -15,7 +15,7 @@ class ConfigsController extends AppController { public $components = array('RequestHandler'); /** - * PP - resolves the issue of not returning all config parameters + * resolves the issue of not returning all config parameters * refer https://github.com/ZoneMinder/ZoneMinder/issues/953 * index method * diff --git a/web/api/app/Controller/EventsController.php b/web/api/app/Controller/EventsController.php index 988c19110..ef569cd7b 100644 --- a/web/api/app/Controller/EventsController.php +++ b/web/api/app/Controller/EventsController.php @@ -14,6 +14,17 @@ class EventsController extends AppController { */ public $components = array('RequestHandler', 'Scaler', 'Image', 'Paginator'); +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('eventPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + /** * index method * @@ -22,6 +33,18 @@ class EventsController extends AppController { */ public function index() { $this->Event->recursive = -1; + + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + if ($this->request->params['named']) { $this->FilterComponent = $this->Components->load('Filter'); @@ -39,7 +62,7 @@ class EventsController extends AppController { $this->Paginator->settings = array( // https://github.com/ZoneMinder/ZoneMinder/issues/995 // 'limit' => $limit['ZM_WEB_EVENTS_PER_PAGE'], - // PP - 25 events per page which is what the above + // 25 events per page which is what the above // default is, is way too low for an API // changing this to 100 so we don't kill ZM // with many event APIs. In future, we can @@ -49,14 +72,14 @@ class EventsController extends AppController { 'limit' => '100', 'order' => array('StartTime', 'MaxScore'), 'paramType' => 'querystring', - 'conditions' => $conditions + 'conditions' => array (array($conditions, $mon_options)) ); $events = $this->Paginator->paginate('Event'); // For each event, get its thumbnail data (path, width, height) foreach ($events as $key => $value) { - // PP - $thumbData = $this->createThumbnail($value['Event']['Id']); - $thumbData =""; + //$thumbData = $this->createThumbnail($value['Event']['Id']); + $thumbData = ""; $events[$key]['thumbData'] = $thumbData; } @@ -71,41 +94,55 @@ class EventsController extends AppController { * @param string $id * @return void */ - public function view($id = null) { - $this->loadModel('Config'); - $configs = $this->Config->find('list', array( - 'fields' => array('Name', 'Value'), - 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) - )); + public function view($id = null) +{ + $this->loadModel('Config'); + $configs = $this->Config->find('list', array( + 'fields' => array('Name', 'Value'), + 'conditions' => array('Name' => array('ZM_DIR_EVENTS')) + )); - $this->Event->recursive = 1; - if (!$this->Event->exists($id)) { - throw new NotFoundException(__('Invalid event')); - } - $options = array('conditions' => array('Event.' . $this->Event->primaryKey => $id)); - $event = $this->Event->find('first', $options); + $this->Event->recursive = 1; + if (!$this->Event->exists($id)) { + throw new NotFoundException(__('Invalid event')); + } - $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; - $event['Event']['BasePath'] = $path; + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); - # Get the previous and next events for any monitor - $this->Event->id = $id; + if (!empty($allowedMonitors)) + { + $mon_options = array('Event.MonitorId' => $allowedMonitors); + } + else + { + $mon_options=''; + } + + $options = array('conditions' => array(array('Event.' . $this->Event->primaryKey => $id), $mon_options)); + $event = $this->Event->find('first', $options); + + $path = $configs['ZM_DIR_EVENTS'].'/'.$this->Image->getEventPath($event).'/'; + $event['Event']['BasePath'] = $path; + + # Get the previous and next events for any monitor + $this->Event->id = $id; $event_neighbors = $this->Event->find('neighbors'); - $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; - $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; + $event['Event']['Next'] = $event_neighbors['next']['Event']['Id']; + $event['Event']['Prev'] = $event_neighbors['prev']['Event']['Id']; - # Also get the previous and next events for the same monitor - $event_monitor_neighbors = $this->Event->find('neighbors', array( + # Also get the previous and next events for the same monitor + $event_monitor_neighbors = $this->Event->find('neighbors', array( 'conditions'=>array('Event.MonitorId'=>$event['Event']['MonitorId']) - )); - $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; - $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + )); + $event['Event']['NextOfMonitor'] = $event_monitor_neighbors['next']['Event']['Id']; + $event['Event']['PrevOfMonitor'] = $event_monitor_neighbors['prev']['Event']['Id']; + + $this->set(array( + 'event' => $event, + '_serialize' => array('event') + )); + } - $this->set(array( - 'event' => $event, - '_serialize' => array('event') - )); - } /** * add method @@ -113,6 +150,13 @@ class EventsController extends AppController { * @return void */ public function add() { + + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + if ($this->request->is('post')) { $this->Event->create(); if ($this->Event->save($this->request->data)) { @@ -131,6 +175,13 @@ class EventsController extends AppController { * @return void */ public function edit($id = null) { + + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } + $this->Event->id = $id; if (!$this->Event->exists($id)) { @@ -157,15 +208,19 @@ class EventsController extends AppController { * @return void */ public function delete($id = null) { + if ($this->Session->Read('eventPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->Event->id = $id; if (!$this->Event->exists()) { throw new NotFoundException(__('Invalid event')); } $this->request->allowMethod('post', 'delete'); if ($this->Event->delete()) { - // PP - lets make sure the frame table entry is removed too - $this->loadModel('Frame'); - $this->Frame->delete(); + //$this->loadModel('Frame'); + //$this->Event->Frame->delete(); return $this->flash(__('The event has been deleted.'), array('action' => 'index')); } else { return $this->flash(__('The event could not be deleted. Please, try again.'), array('action' => 'index')); diff --git a/web/api/app/Controller/HostController.php b/web/api/app/Controller/HostController.php index ee5ecd30f..57b210a7e 100644 --- a/web/api/app/Controller/HostController.php +++ b/web/api/app/Controller/HostController.php @@ -101,7 +101,10 @@ class HostController extends AppController { function getVersion() { $version = Configure::read('ZM_VERSION'); - $apiversion = Configure::read('ZM_API_VERSION'); + // not going to use the ZM_API_VERSION + // requires recompilation and dependency on ZM upgrade + //$apiversion = Configure::read('ZM_API_VERSION'); + $apiversion = '1.0'; $this->set(array( 'version' => $version, diff --git a/web/api/app/Controller/MonitorsController.php b/web/api/app/Controller/MonitorsController.php index 3aeb31aa8..9ab7461f9 100644 --- a/web/api/app/Controller/MonitorsController.php +++ b/web/api/app/Controller/MonitorsController.php @@ -16,6 +16,19 @@ class MonitorsController extends AppController { */ public $components = array('Paginator', 'RequestHandler'); + +public function beforeFilter() { + parent::beforeFilter(); + $canView = $this->Session->Read('monitorPermission'); + if ($canView =='None') + { + throw new UnauthorizedException(__('Insufficient Privileges')); + return; + } + +} + + /** * index method * @@ -23,7 +36,17 @@ class MonitorsController extends AppController { */ public function index() { $this->Monitor->recursive = 0; - $monitors = $this->Monitor->find('all'); + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + + if (!empty($allowedMonitors)) + { + $options = array('conditions'=>array('Monitor.Id'=> $allowedMonitors)); + } + else + { + $options=''; + } + $monitors = $this->Monitor->find('all',$options); $this->set(array( 'monitors' => $monitors, '_serialize' => array('monitors') @@ -42,7 +65,21 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - $options = array('conditions' => array('Monitor.' . $this->Monitor->primaryKey => $id)); + $allowedMonitors=preg_split ('@,@', $this->Session->Read('allowedMonitors'),NULL, PREG_SPLIT_NO_EMPTY); + if (!empty($allowedMonitors)) + { + $restricted = array('Monitor.' . $this->Monitor->primaryKey => $allowedMonitors); + } + else + { + $restricted = ''; + } + + $options = array('conditions' => array( + array('Monitor.' . $this->Monitor->primaryKey => $id), + $restricted + ) + ); $monitor = $this->Monitor->find('first', $options); $this->set(array( 'monitor' => $monitor, @@ -57,6 +94,13 @@ class MonitorsController extends AppController { */ public function add() { if ($this->request->is('post')) { + + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthotizedException(__('Insufficient privileges')); + return; + } + $this->Monitor->create(); if ($this->Monitor->save($this->request->data)) { $this->daemonControl($this->Monitor->id, 'start', $this->request->data); @@ -78,7 +122,11 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists($id)) { throw new NotFoundException(__('Invalid monitor')); } - + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } if ($this->Monitor->save($this->request->data)) { $message = 'Saved'; } else { @@ -89,9 +137,8 @@ class MonitorsController extends AppController { 'message' => $message, '_serialize' => array('message') )); - // PP - restart this monitor after change - $this->daemonControl($this->Monitor->id, 'restart', $this->request->data); - + // - restart this monitor after change + $this->daemonControl($this->Monitor->id, 'restart', $this->request->data); } /** @@ -106,6 +153,11 @@ class MonitorsController extends AppController { if (!$this->Monitor->exists()) { throw new NotFoundException(__('Invalid monitor')); } + if ($this->Session->Read('systemPermission') != 'Edit') + { + throw new UnauthorizedException(__('Insufficient privileges')); + return; + } $this->request->allowMethod('post', 'delete'); $this->daemonControl($this->Monitor->id, 'stop'); diff --git a/web/api/app/Model/User.php b/web/api/app/Model/User.php new file mode 100644 index 000000000..8ef18d131 --- /dev/null +++ b/web/api/app/Model/User.php @@ -0,0 +1,31 @@ + array( + 'Help' => "This field has certain limitations when used for non-local devices.~~ ". + "Failure to adhere to these limitations will cause a delay in live video, irregular frame skipping, ". + "and missed events~~". + "For streaming IP cameras, do not use this field to reduce the frame rate. Set the frame rate in the". + " camera, instead. You can, however, use a value that is slightly higher than the frame rate in the camera. ". + "In this case, this helps keep the cpu from being overtaxed in the event of a network problem.~~". + "Some, mostly older, IP cameras support snapshot mode. In this case ZoneMinder is actively polling the camera ". + "for new images. In this case, it is safe to use thie field." ), // 'LANG_DEFAULT' => array( diff --git a/web/skins/classic/js/dark.js b/web/skins/classic/js/dark.js index 06a940482..f0561c78f 100644 --- a/web/skins/classic/js/dark.js +++ b/web/skins/classic/js/dark.js @@ -42,13 +42,13 @@ var popupSizes = { 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 92 }, - 'group': { 'width': 360, 'height': 180 }, - 'groups': { 'width': 440, 'height': 220 }, + 'group': { 'width': 360, 'height': 300 }, + 'groups': { 'width': 540, 'height': 420 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, 'logout': { 'width': 260, 'height': 100 }, - 'monitor': { 'width': 525, 'height': 700 }, + 'monitor': { 'width': 600, 'height': 780 }, 'monitorpreset':{ 'width': 440, 'height': 200 }, 'monitorprobe': { 'width': 500, 'height': 240 }, 'monitorselect':{ 'width': 160, 'height': 200 }, diff --git a/web/skins/classic/js/flat.js b/web/skins/classic/js/flat.js index 2cee14dd2..a90fa9987 100644 --- a/web/skins/classic/js/flat.js +++ b/web/skins/classic/js/flat.js @@ -42,13 +42,13 @@ var popupSizes = { 'frame': { 'addWidth': 32, 'minWidth': 384, 'addHeight': 100 }, 'frames': { 'width': 500, 'height': 600 }, 'function': { 'width': 300, 'height': 140 }, - 'group': { 'width': 360, 'height': 180 }, - 'groups': { 'width': 440, 'height': 220 }, + 'group': { 'width': 360, 'height': 300 }, + 'groups': { 'width': 540, 'height': 420 }, 'image': { 'addWidth': 48, 'addHeight': 80 }, 'log': { 'width': 1080, 'height': 720 }, 'login': { 'width': 720, 'height': 480 }, 'logout': { 'width': 260, 'height': 100 }, - 'monitor': { 'width': 550, 'height': 700 }, + 'monitor': { 'width': 600, 'height': 780 }, 'monitorpreset':{ 'width': 440, 'height': 200 }, 'monitorprobe': { 'width': 500, 'height': 240 }, 'monitorselect':{ 'width': 160, 'height': 200 }, diff --git a/web/skins/classic/views/monitor.php b/web/skins/classic/views/monitor.php index 115869616..31ffd9232 100644 --- a/web/skins/classic/views/monitor.php +++ b/web/skins/classic/views/monitor.php @@ -711,9 +711,19 @@ switch ( $tab ) - - +  () +  () + + + +