On Ubuntu do the following:
# sudo apt-get install ruby1.9.3 libsqlite3-dev libavahi-compat-libdnssd-dev # sudo ruby1.9.3 -S gem install rubynas --no-ri --no-rdoc44 # sudo rubynas install
Currently not implemented but in the future the user can supply these options:
# sudo rubynas install --admin admin --domain rubynas.com --password secret
The rubynas api will be exposed using the mdns service. Typically its available on https://localhost:5100/api/. Maybe replace
localhost by the name of your server.
To start developing simply start foreman:
Login with one of the users in the ldap:
email: firstname.lastname@example.org or email@example.com password: secret
Run the tests
Since the tests depend on the LDAP you have to start it first with:
Then use either:
This project has a typical rails layout plus:
- sandbox place for local ldap server and the like
The following section describes how shares "should be/are" implemented in rubynas.
The general idea more or less oriented how mac os x is handling shares with a bit more flexibility. Here a short reminder on that:
Basically we can create a share which has a location and users or groups that can access it. Unlike mac os x the user can specify in which way the share is exposed. In other words it should be possible to tell per share if it can be accessed by AFP, SMB etc.
Bases on the idea above we got the following structure:
- Name (String) Name of the share
- Path (String) Posix path to the share
- Permissions (Array) A list with users, groups that have access to the share
- Services (Array) A list of services that should expose the share. Imagine the following list:
- Options (Hash) A hash of options extra for each of the sharing services.
A Permission is either based on a group or user. The reference to the ldap user and ldap group has to be made using the common name.
- Read (Boolean) true if the permissions grants read access
- Write (Boolean) true if the permission grants write access
- User (LdapUser) the ldap user that is permitted
- Group (LdapGroup) the ldap user that is permitted
To actually apply the share configuration to the services there are several layers which control the sharing service (aftp, smb, ...) and it's configuration. Here a quick visualization, of the layers (the api is the initiator):
The shares API which is instrumented by the frontend, it manipulates the share models. This means creating, updating and changing shares. After the API did a manipulation to the shares it will call
ShareService.update_all which will trigger an update on each and every service.
Speaking of a sharing service: It needs to inherit from
ShareService and implement the
#update! method like this example:
class AfpShareService < ShareService service_name :netatalk def update! if AfpServiceConfiguration.update restart # supplied by base class end end end
The update basically creates a new rendered version of the configuration and makes a hash on the content. Next it checkes if the old config has the same hash, if it does,
false is returned and nothing happens. If the hash is different the configuration will be replaced and update returns
true so that the service is going to be restarted.
In order for a
ShareService to be so simple a lot of magic needs to happen in the background. This will be described further below. Now regarding the configuration:
class AfpServiceConfiguration < ShareServiceConfiguration config_file 'afp.conf' def time_machine(share) share.options[self][:time_machine] ? 'yes' : 'no' end end
And here is the template for the configuration above:
; ; Netatalk 3.x configuration file ; [Global] ; log level = default:maxdebug log file = /var/log/netatalk uam list = uams_dhx.so uams_dhx2.so [Homes] basedir regex = /home <% @shares.each do |share| %> [<%= @share.name %>] path = <%= @share.path %> time machine = <%= time_machine(share) %> <% end %>
The magic behind scenes is basically done by the superclasses. They know how to start the a service and where the configuration files are placed. Main part of that magic is the automatic system and configuration detection. This is described in the section OS.
Config service (idea)
The configuration needs administrative access to the system. Because every system has a different way to manage processes and a different way to configure this processes, this part is moved to a different project to keep the core clean from system and os details. This examples illustrates a possible restful interface
GET http://service.local/services => each service with status POST http://service.local/services/netatalk/start => 200 started, 403 already started POST http://service.local/services/netatalk/restart => 200 restarted POST http://service.local/services/netatalk/stop => 200 stopped, 403 already stopped GET http://service.local/services/netatalk => Status PUT http://service.local/services/netatalk/config => 200 updated, 302 not updated