Trying Out Shoop eCommerce

posted Jul 11, 2015, 12:39 PM by Pasi Orovuo   [ updated Jul 11, 2015, 12:40 PM ]

Shoop is a new eCommerce platform developed and open sourced by Anders Innovation ( It caught my attention because the company behind it has been some fairly interesting projects and is packed with promising talent. Additionally, it's based on Python and Django - the platforms I've been developing mainly on lately.

Shoop is its early stages and beginning of its lifecycle, so it seems to be a bit rough around the edges and the documentation published is not exactly comprehensive. I gave it a test run and here are the notes written during process.

Downloading and Installing

There does not seem to be official releases (or maybe it's their development model) and Shoop is only available via Github ( First though, create a virtualenv to accommodate the project and activate it:

pormb:Sites por$ virtualenv shooptest
New python executable in shooptest/bin/python
Installing setuptools, pip...done.
pormb:Sites por$ cd shooptest/
pormb:shooptest por$ source bin/activate
(shooptest)pormb:shooptest por$
Then clone the repository, and install Shoop and its dependencies.
(shooptest)pormb:shooptest por$ git clone
Cloning into 'shoop'...
remote: Counting objects: 1752, done.
remote: Total 1752 (delta 0), reused 0 (delta 0), pack-reused 1752
Receiving objects: 100% (1752/1752), 2.43 MiB | 1.53 MiB/s, done.
Resolving deltas: 100% (705/705), done.
Checking connectivity... done.
(shooptest)pormb:shooptest por$ pip install shoop/
Unpacking ./shoop
... *** bunch of lines snipped ***
Successfully installed Babel Django django-bootstrap3 django-countries django-enumfields django-filer django-jinja django-mptt django-parler django-polymorphic django-registration-redux django-timezone-field djangorestframework factory-boy fake-factory jsonfield Markdown pytz requests six shoop enum34 easy-thumbnails Unidecode jinja2 pillow markupsafe
Cleaning up...
(shooptest)pormb:shooptest por$
Ok! Now we have Shoop installed. The official documentation (at the time of writing) states that "After this, you can begin setting up a Django project using whichever standards you fancy." To me, based on that, it wasn't at all clear on how to proceed. :-)

Setting Up the Project

Without the help of further instructions, I went on the only way I know - by starting a new Django project with django-admin.
(shooptest)pormb:shooptest por$ django-admin startproject shooptest
(shooptest)pormb:shooptest por$ cd shooptest/
Now that the project is ready, our freshly installed Shoop needs to be incorporated into it somehow. Documentations mentions modules shoop.core, shoop.front, and shoop.admin. Shoop.core must be a basic requirement for both modules, and in order for the front to make any sense, I'd imagine Shoop needs to be configured, products established etc. So shoop.admin needs to be configured first. So I fired PyCharm (by far the best Python IDE I've tried, check it out and went on to set up the project.

Basic configuration

As with all Django projects, external modules need to be included in INSTALLED_APPS setting. So went to add modules mentioned earlier in shooptest/

# Application definition



Now, if you try to migrate the project, an error is displayed:

(shooptest)pormb:shooptest por$ python migrate
Traceback (most recent call last):
  File "", line 10, in <module>
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/django/core/management/", line 338, in execute_from_command_line
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/django/core/management/", line 312, in execute
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/django/", line 18, in setup
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/django/apps/", line 85, in populate
    app_config = AppConfig.create(entry)
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/django/apps/", line 141, in create
    return cls(app_name, app_module)
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/shoop/apps/", line 113, in __init__
  File "/Users/por/Sites/shooptest/lib/python2.7/site-packages/shoop/apps/", line 141, in _check_required_installed_apps
    raise ImproperlyConfigured("%s requires the following INSTALLED_APPS: %s" % (, information))
django.core.exceptions.ImproperlyConfigured: shoop.core requires the following INSTALLED_APPS: easy_thumbnails (required), filer (required)
(shooptest)pormb:shooptest por$
Aha! shoop.core requires two additional modules: easy_thumbnails and filer. Let's add them to the installed modules in shooptest/ In the example below I've also added bootstrap3, which is required by shoop.admin. So instead of looking again at an error like the one above, add it as well.



Now that all the required modules are included we should be ready migrate and browse the admin site? Right? Not quite, few things are still missing as indicated by the error.
(shooptest)pormb:shooptest por$ python migrate
Traceback (most recent call last):
django.core.exceptions.ImproperlyConfigured: The DjangoTemplates engine was encountered in your template configuration before Django-Jinja. This configuration will not work correctly with Shoop.
(shooptest)pormb:shooptest por$
This is something that bit me. Django-Jinja must be added into TEMPLATES in shooptest/, and DjangoTemplates must be kept intact. At first I replaced DjangoTemplates with Django-Jinja, and while testing the admin site, I got errors in taxes. So add Django-Jinja before DjangoTemplates:

'BACKEND': 'django_jinja.backend.Jinja2',
'APP_DIRS': True,
'match_extension': '.jinja',
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'context_processors': [

Okay, that we've cleared that out of the way, can we migrate to set up the database etc?

(shooptest)pormb:shooptest por$ python migrate

Operations to perform:
  Synchronize unmigrated apps: staticfiles, shoop_admin, messages, bootstrap3
  Apply all migrations: filer, shoop, sessions, admin, shoop_front, auth, contenttypes, easy_thumbnails
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
  Installing custom SQL...
Running migrations:
  Rendering model states... DONE
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying easy_thumbnails.0001_initial... OK
  Applying easy_thumbnails.0002_thumbnaildimensions... OK
  Applying filer.0001_initial... OK
  Applying filer.0002_auto_20150606_2003... OK
  Applying sessions.0001_initial... OK
  Applying shoop.0001_initial... OK
  Applying shoop_front.0001_initial... OK
(shooptest)pormb:shooptest por$
It seems so - nice! We should be ready to run the development server - but first, we need the admin user.

(shooptest)pormb:shooptest por$ python createsuperuser

Username (leave blank to use 'por'): admin
Email address:  
Password (again): 
Superuser created successfully.
(shooptest)pormb:shooptest por$ python runserver

Performing system checks...

System check identified no issues (0 silenced).
July 11, 2015 - 19:17:09
Django version 1.8.2, using settings 'shooptest.settings'
Starting development server at
Quit the server with CONTROL-C.
Okay, the server is running, but we still need to configure URL routing in order to reach Shoop admin UI. Open up and add the route for shoop-admin. Please note the required namespace as well. 

from django.conf.urls import include, url
from django.contrib import admin

import shoop.admin.urls

urlpatterns = [
url(r'^admin/', include(,
url( r'^shoop-admin/', include( shoop.admin.urls, namespace = 'shoop_admin' ) ),
Now Django should be able to find Shoop's admin UI. Browse to and voilá! You should be greeted by Shoop's login Window.

Shoop Login


Final words

Shoop seems to contain a bunch of (undocumented) additional modules. They bring additional options into admin UI. Play around with them and what they do.

# Application definition




How to change DHCP provided DNS servers on a Huawei B593s-22

posted Dec 13, 2014, 3:12 AM by Pasi Orovuo   [ updated Dec 13, 2014, 11:51 PM ]

I my previous post Getting SSH access on a Huawei B593s-22 I went on a quest to change the DHCP provided DNS servers on my Huawei B593s-22. Getting SSH access to poke around the filesystem came about a bit tricky, so I figured it merits it's own post. Once I gained root access on the box, I went on to look on how to change the DNS servers. There are several places I looked into and things I tried - which I don't bother you with.

With root FTP explained in the previous post, download and edit /app/curcfg.xml. It's the base that's used to create /var/dhcp/dhcps/config upon each boot.

DNS servers can be found at Xpath: /InternetGatewayayDevice/LANDevice/LANDeviceInstance/LANHostConfigManagement/@DNSServers


$ xmllint --xpath '/InternetGatewayDeviceConfig/InternetGatewayDevice/LANDevice/LANDeviceInstance/LANHostConfigManagement/@DNSServers' curcfg.xml; echo ""

Getting SSH access on a Huawei B593s-22

posted Dec 13, 2014, 1:28 AM by Pasi Orovuo   [ updated Dec 13, 2014, 1:30 AM ]

I have Huawei's B593s-22 4G router as the main connection at home. It's quite neat device, and if 4G connectivity is available, fast too. This morning I wanted to browse on my iPhone (which is connected to WLAN provided by B593s-22), but got a DNS query failure notification. Strange - it's somewhat unlikely that Amazon would have that kind of problems during the christmas sale, so I decided to drop WLAN and try plain cellular (and thus drop the Huawei from the equation). was working fine - a bit of a WTF moment. What exactly does B593s-22 do on my DNS queries for Amazon? Which DNS servers does it use?! We'll, as it seems, it's not that easy.

DHCP clients get B593s-22's IP as the DNS server and It's not possible to see or choose which DNS servers B593s-22 uses or gives to DHCP clients from the web user interface. In order to gain further insight, I would need SSH access on the box. Unfortunately the web interface admin password did not work.

After Googling around I came across this site which mentions a USB/FTP hack. The instructions are for B593s-12, and the -22 has been changed significantly, so the instructions won't work. Huawei's track record in security is terrible, so I figured it should still be easy to gain access. So I went ahead and connected a FAT-32 formatted 16 GB USB stick in the device and went on to enable the FTP interface:

Then went on to add a user:

Bummer - according to instructions on above site I would need to type ../../ into the path input box to gain access to the root filesystem. Huawei's engineers have put their best into prevent tampering the box and have changed the input box to a Select button which enables the users to choose from existing folders on the USB stick. Damn....

Well - as mentioned earlier - Huawei's track record in security is shit, so I figured that it's quite possible that the checks and limitations are only client side, and I would only need to get past them. After looking into and fiddling with the source code of the page and various Javascript files it includes, I figured that simple DOM manipulation could be the method. Essentially - I would need to include ../../ in the selection list presented by the Select button. i.e.

This can be achieved easily in Google Chrome by just right-clicking an element (i.e. .Spotlight-V100) and choosing Inspect Element. Chrome spits out the source of the page which you can edit to your liking. The edits I made were:

So basically I just duplicated the <li id="file_tree_div3"> and removed the lastExpandable related classes from the third item. Then went on to change the newly created file_tree_div3 to file_tree_div4 and changed the path on the span and pre elements. Nice and easy, and now the file selection looked the way I wanted it to look and submitted the user. The result was promising:

Time to test... I went on and FTP'd onto the box.
pormb:Downloads por$ ftp
Connected to
220 bftpd %v at ready.
Name ( ftp
331 Password please.
230 User logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls
229 Entering extended passive mode (|||50544|)
150 BINARY data connection established.
drwxrwx--x   1 0        0        4096 Nov 28 17:53 app
lrwxrwxrwx   1 0        0        14 Jan 01  1970 bin -> system/atp/bin
drwxrwx---   2 1000     2001     0 Jan 01  1970 cache
dr-x------   2 0        0        0 Jan 01  1970 config
drwxrwxrwx   1 0        0        4096 Dec 18  2013 cpedata
drwxrwx--x   1 1000     1000     4096 Jan 01  2013 data
-rw-r--r--   1 0        0        118 Jan 01  1970 default.prop
drwxr-xr-x  10 0        0        0 Dec 13 10:16 dev
lrwxrwxrwx   1 0        0        15 Jan 01  1970 etc -> /system/atp/etc
-rw-rw-rw-   1 0        0        0 Jan 01  2013 hsvelog.txt
lrwxrwxrwx   1 0        0        12 Jan 01  1970 html -> cpedata/html
-rwxr-x---   1 0        0        94168 Jan 01  1970 init
-rwxr-x---   1 0        0        1677 Jan 01  1970 init.goldfish.rc
-rwxr-x---   1 0        0        15298 Jan 01  1970 init.rc
lrwxrwxrwx   1 0        0        14 Jan 01  1970 lib -> system/atp/lib
drwxr-xr-x   2 0        0        0 Jan 01  1970 media
drwxr-xr-x   4 0        0        0 Dec 13 10:16 mnt
drwxr-xr-x   1 0        0        4096 Jan 01  1970 online
dr-xr-xr-x 123 0        0        0 Jan 01  1970 proc
drwx------   2 0        0        0 Dec 18  2013 root
drwxr-x---   2 0        0        0 Jan 01  1970 sbin
drwxr-xr-x  12 0        0        0 Jan 01  1970 sys
drwxrwxrwx   1 0        0        4096 Dec 18  2013 system
drwxr-xr-x   6 0        0        0 Jan 01  2013 tmp
drwxr-xr-x   2 0        0        0 Jan 01  1970 tts
-rw-r--r--   1 0        0        0 Jan 01  1970 ueventd.goldfish.rc
-rw-r--r--   1 0        0        3764 Jan 01  1970 ueventd.rc
lrwxrwxrwx   1 0        0        14 Jan 01  1970 usr -> system/atp/usr
drwxr-xr-x  25 0        0        0 Dec 13 11:17 var
lrwxrwxrwx   1 0        0        15 Jan 01  1970 xbin -> system/atp/sbin
226 Directory list has been submitted.
Nice, Huawei. Your security record still is shit.

From either here or here I had already learnt that the plain text SSH passwords can be found in /var/sshusers.cfg. I went on to download it and yes, there it was!

pormb:~ por$ ssh admin@
admin@'s password: 
-----Welcome to ATP Cli------

BusyBox vv1.9.1 (2013-12-18 15:31:27 CST) built-in shell (ash)
Enter 'help' for a list of built-in commands.

Now I have an SSH shell, but I've still to solve the DNS issue...

Import Passwords from Passpack to F-Secure Key

posted Jan 9, 2014, 10:59 AM by Pasi Orovuo   [ updated Jan 9, 2014, 1:05 PM ]

I've been a long time user of Passpack. After F-Secure released it's Key password manager ( I decided to give it a shot, and found it very appealing due to it's simplicity.

And after using it for a while I decided give up Passpack - there was a problem though... F-Secure Key did not support import of CSV or such formats. Fortunately, it supports few other formats, so it is possible to convert from Passpack's CSV to one of the other formats. Below is the script I wrote for the task. Basically, you can format just about any export format to the simple CSV format:
  • UTF-8 encoding
  • No header line
  • Comma as the separator
  • Double quote " as the quoting character
  • Fields should be in the following order
    1. title
    2. username
    3. password
    4. url
    5. tags (ignored!)
    6. note
    7. email (will be concatenated with note in the output xml)

Usage: input.csv > output.xml

And output.xml is ready to be imported in F-Secure Key.

# -*- coding: utf-8 -*-

import lxml.etree
import sys
import csv

if len( sys.argv ) != 2:
    print '%s csv.txt' % sys.argv[0]

def utf8_csv_reader( utf8_data, dialect = csv.excel, **kwargs ):
    csv_reader = csv.reader( utf8_data, dialect = dialect, **kwargs )
    for row in csv_reader:
        yield [ unicode( cell, 'utf-8' ) for cell in row ]

root = lxml.etree.Element( 'passwordsafe' )
with open( sys.argv[1], 'rb' ) as csvfile:
    lines = utf8_csv_reader( csvfile, delimiter = ',', quotechar = '"' )
    for line in lines:
        ( name, uid, pwd, link, tags, note, email ) = line
        notes = []
        if len( email ) > 0:
            notes.append( 'Email: %s' % ( email ) )
        if len( note ) > 0:
            notes.append( note )
        entry = lxml.etree.SubElement( root, 'entry' )
        lxml.etree.SubElement( entry, 'title' ).text    = name
        lxml.etree.SubElement( entry, 'url' ).text      = link
        lxml.etree.SubElement( entry, 'username' ).text = uid
        lxml.etree.SubElement( entry, 'password' ).text = pwd
        lxml.etree.SubElement( entry, 'notes' ).text    = '\n\n'.join( notes )

print lxml.etree.tostring( root, pretty_print = True, xml_declaration = True, encoding = 'utf-8' )

Apache LDAP Authentication

posted Oct 29, 2013, 10:41 AM by Pasi Orovuo

A simple, concise description of Apache configuration with LDAP authentication. I wish I had found this two hours ago:

Logging on a Juniper SRX

posted Oct 9, 2013, 12:50 PM by Pasi Orovuo   [ updated Jul 19, 2014, 1:15 AM ]

How to do simple logging on an SRX?

set system syslog archive size 100k
set system syslog archive files 3
set system syslog file blocked-traffic any any
set system syslog file blocked-traffic match RT_FLOW_SESSION_DENY
set system syslog file accepted-traffic any any
set system syslog file accepted-traffic match RT_FLOW_SESSION_CREATE

Then on your policy rule have for example

set security policies from-zone trust to-zone untrust policy then-log-and-drop match source-address any
set security policies from-zone trust to-zone untrust policy then-log-and-drop match destination-address any
set security policies from-zone trust to-zone untrust policy then-log-and-drop match application any
set security policies from-zone trust to-zone untrust policy then-log-and-drop then reject
set security policies from-zone trust to-zone untrust policy then-log-and-drop then log session-init

And with 

show log blocked-traffic
show log accepted-traffic

see what's happening.

Alternatively, monitor the packet flow on the device:

set security flow traceoptions file flow-trace
set security flow traceoptions file size 1m
set security flow traceoptions file files 2
set security flow traceoptions file world-readable
set security flow traceoptions flag basic-datapath
set security flow traceoptions packet-filter filter1 destination-prefix

And with

show log flow-trace

examine the flow.

For reference, here's an image that describes the processing order of different modules within JunOS. Original available in here:

Happy troubleshooting!

Cisco Stencils for Evolus Pencil

posted Feb 3, 2013, 10:35 AM by Pasi Orovuo   [ updated Feb 3, 2013, 10:36 AM ]

I'm very accustomed of using Cisco Network Stencils (available in here) in MS Office Visio at work. On my personal computer I'm unfortunately limited to free software, such as Evolus Pencil. Pencil, on the other hand, is missing stencils for network diagramming which makes it pretty much useless for network diagrams.

To fix this, I've converted the Cisco Stencil package to the format Evolus' Pencil uses. File available below.

I do not own any rights to the images - they belong to Cisco Systems.

Disable GIMP Splash Screen on OS X

posted Jan 11, 2013, 6:17 PM by Pasi Orovuo

GIMP's finally got an OS X native build - yay! There's one thing, that I found very annoying. Each time you start the program, the splash screen is displayed on top of the window stack covering other windows. And it's not possible to Option-Tab it away. There's help though.

It's possible to disable the splash screen entirely, by editing the startup script. So navigate to /Applications/ and edit file called GIMP.

At he bottom you'll see something like this:

if [ "x$GTK_DEBUG_SHELL" != "x" ]; then
    exec bash

    $EXEC "$bundle_contents/MacOS/$name-bin"
Add --no-splash on the second last line:

if [ "x$GTK_DEBUG_SHELL" != "x" ]; then
    exec bash

    $EXEC "$bundle_contents/MacOS/$name-bin" --no-splash

Drupal Multisite Performance

posted Nov 12, 2012, 11:36 AM by Pasi Orovuo   [ updated Nov 12, 2012, 11:38 AM ]

A while back I heard that Drupal performs badly on multi site (around 50 or so sites) environments. It made my wonder what exactly makes the difference. Basically, it's just a LAMP stack, and 50 site folders shouldn't make a significant difference.

I decided to test it out... for the test I created a fresh Drupal 6.15 installation, and generated 1, 20, 50 and 100 sites within it. Each site containing 200 nodes (with paragraphs from H.G. Wells' War of the Worlds as content, thanks to Project Gutenberg).

Each installation was hammered with 50 concurrent users each requesting 5 pages (with Siege, available in here).

And the result? As expected...

With 1-50 sites the performance has no difference what so ever. Most of the time is spent waiting for the database, so caching database objects is the easiest and probably most effective way to improve performance. After 50 sites my humble test machine runs out of memory so the ball is dropped pretty much immediately.

The scripts I used for testing and generating are attached below.

Drupal XMLRPC: Create Nodes with Python

posted Nov 9, 2012, 10:20 AM by Pasi Orovuo   [ updated Nov 9, 2012, 10:21 AM ]

As a part of another project, I needed to create a lot (hundreds) of nodes in Python programmatically. The key to this is Service API, available in here: Links on the page led me to this tutorial which uses a cookie aware transport available from here.

To my disappointment, the CookieTransport code doesn't work on Python 2.7. I got an error about missing getreply() method. Inspection of the code made me wonder if there would be a better way to implement the same functionality, but without copying large chunks of the parent class code to the subclass. A solution which appeared to me as rather non-oop... Below you'll find the code which works nicely on Python 2.7.

Otherwise the linked tutorial is very useful. Another thing to note is that when using Services API, I needed to grant access to Services API for Anonymous user. API itself requires authentication regardless.


import xmlrpclib

class CookieTransport( xmlrpclib.Transport ):
    def __init__( self ):
        xmlrpclib.Transport.__init__( self )
        self.cookies = {}
    def set_cookie( self, key, value ):
        self.cookies[key] = value
    def unset_cookie( self, key ):
        if self.cookies.haskey( key ):
            del self.cookies[key]
    def get_cookies( self ):
        cookies = []
        for key in self.cookies.keys():
            cookies += [ '%s=%s' % ( key, self.cookies[key] ) ]
        return cookies
    def get_http_cookies( self ):
        cookies = self.get_cookies()
        return '; '.join( cookies ) if len( cookies ) > 0 else None
    def send_host( self, connection, host ):
        cookies = self.get_http_cookies()
        if cookies != None:
            connection.putheader( 'Cookie', cookies )
        return xmlrpclib.Transport.send_host( self, connection, host )

    def parse_response( self, response ):
        for header in response.getheaders():
            if header[0] == 'set-cookie':
                cookie = header[1].split( ';' )[0].strip()
                key, value = cookie.split( '=' )
                self.cookies[key] = value
        return xmlrpclib.Transport.parse_response( self, response )

1-10 of 13